Report non-JSON API responses as they came - #149
Merged
Conversation
The GraphQL client parsed every response body as JSON regardless of what came back. A gateway error such as an Envoy 503, whose body is plain text, therefore surfaced as "Unexpected token 'u', "upstream c"... is not valid JSON", which says nothing about the status or the failure, and discarded the body before anything was printed. The body is now read as text and parsed explicitly, and a body that is not a JSON object is reported as it came. The check is on the payload rather than on the response status because the API answers an invalid query with status 400 and a payload, and access denied with status 200 and a payload, so the status does not tell whether the body carries structured errors. A test pins that behaviour. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
👋 @tiagobarros01
☝️ Lastly, the title for the commit will come from the pull request title. So please provide a descriptive title that summarizes the changes in 50 characters or less using the imperative mood. |
commit: |
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.
Problem
The GraphQL client called
response.json()on every response, whatever came back:When the API is behind a gateway that answers with a plain-text error — an Envoy 503 whose body is
upstream connect error or disconnect/reset before headers. reset reason: connection termination— the parse blows up and the CLI prints:That message names neither the status nor the failure, and the body is discarded before anything reaches the user. V8 reveals only the first 10 characters of the input, so
upstream cis all that survives — not enough to tell a connection reset from a circuit breaker or a rate limit.This surfaced in a downstream project whose CI hit a transient gateway error during
npm ci. The parse error was the only clue, and it pointed nowhere near the cause.Change
The body is read as text and parsed explicitly. A body that is not a JSON object is reported as it came, so the gateway's own message reaches the user.
Why not check
response.okChecking the status looks simpler but breaks the structured error path. Probing the API directly:
{"data":{...}}{"errors":[{"extensions":{"type":"...#invalid-input","status":400,...}}]}{"errors":[{"extensions":{"type":"...#access-denied","status":403,...}}]}A
!response.okguard would throw on the 400 before the payload is read, discarding theProblem— its type, detail and violations — and replacingInvalid inputwith a raw JSON dump. It would also miss the access-denied case entirely, since that arrives as 200. The status simply does not indicate whether the body carries structured errors, so the check is on the payload instead. A test pins the 400-with-payload behaviour so the regression cannot be reintroduced quietly.Tests
test/infrastructure/graphql/fetchGraphqlClient.test.tsis new — the client had no test file. It covers the gateway error, an HTML error page, an empty body, a non-object payload, the 400-with-payload path, and the existing valid-payload and token-provider behaviour.fetchGraphqlClient.tsis at 100% coverage.Validation
npm run validate,npm run build,npm test(621 passed, 38 suites) andnpm run lintall pass.Out of scope
Two related issues remain, both outside this change:
postinstallchained with&&carries on andnpm cireports success with a broken tree.node_modules/@croct/contentis cleared before fetching, so a failed run leaves the tree emptier than it found it.Together those turn a transient gateway blip into a green install that fails much later with an unrelated-looking error. Happy to follow up on either.
🤖 Generated with Claude Code