Conversation
The fallback error middleware called `res.status().send()` unconditionally, which threw `Cannot set headers after they are sent to the client` when upstream middleware (e.g. cube's api-gateway handler) had already written the response. The thrown error masked the original stack. Bail out via `next(err)` when `res.headersSent` so the default express handler can finish tearing down the connection. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
yasirali179
approved these changes
Apr 23, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
res.headersSent→next(err)inservices/cubejs/index.js.res.status().send()on a response whose headers have already been flushed.Context
Observed in production logs on
synmetrix-cubejs:The upstream cube api-gateway error handler already sends its own response, then forwards via
next(err). The trailingapp.use((err, req, res, next) => ...)inindex.jsthen tries to write again and throws. This masks the original error (the surfaced message was a hasura auth rejection, not a header issue) and adds log noise.Not the cause of any crash — cube's orchestrator handles the re-throw — but it's obscuring real errors.
Test plan
/cubejs-api/v1/loadwith no auth, confirm response is still 403/401 (handled upstream) and noCannot set headersline appears in logs.src/routes/, confirm it still reaches this fallback handler and returns 500 witherr.message.🤖 Generated with Claude Code