Skip to content

Conversation

ronjalh
Copy link
Member

@ronjalh ronjalh commented Oct 22, 2026

Summary

Invalid queries came back as INTERNAL_SERVER_ERROR with the message "Internal server error". They now get their real code, for example GRAPHQL_VALIDATION_FAILED, while unexpected errors in resolvers are still hidden.

Closes #93

Cause

Since 84116e6, formatError treated every error that Apollo wraps in another error (original !== error) as unknown and hid it. Apollo wraps its own errors too. I ran formatError against a real Apollo Server (5.5.1) and found that more than the issue lists was hidden:

Request Code before Code now
Unknown field, or a query that is too deep INTERNAL_SERVER_ERROR GRAPHQL_VALIDATION_FAILED
Syntax error INTERNAL_SERVER_ERROR GRAPHQL_PARSE_FAILED
Missing or invalid variable INTERNAL_SERVER_ERROR BAD_USER_INPUT
Unknown operationName INTERNAL_SERVER_ERROR OPERATION_RESOLUTION_FAILURE
A resolver throws an unexpected error INTERNAL_SERVER_ERROR (hidden) unchanged, still hidden

The old unit test built its error without originalError, which is not how Apollo sends them, so it passed while the server test failed.

What is in it

  • formatError decides in three steps: (1) our own errors keep their code, (2) an error with one of Apollo's client error codes is let through with its message, (3) everything else is hidden and logged, as before.
  • APOLLO_CLIENT_ERROR_CODES lists every code by name (ApolloServerErrorCode.*), with a comment on each. INTERNAL_SERVER_ERROR is left out on purpose.
  • docs/API.md: the error table and a short text about what is let through and what is hidden. docs/AI_USAGE.md: a new section.

Choices

Question Choice Why
Allow a list of codes, or hide only INTERNAL_SERVER_ERROR? A list of allowed codes An unknown error must not leak by default (the Apollo docs and the GraphQL Foundation's error masking guide both point to a list of safe codes). The existing test "hides an unexpected error even when it carries another code" keeps passing.
How to make sure a new Apollo code is not forgotten? A test over ApolloServerErrorCode The bug in #93 came from a rule that hid too much. If Apollo adds a code, the test fails until someone decides if it is the client's or the server's doing.
Detect Apollo's errors by structure (no path) instead of by code? No It works, but depends on graphql-js internals. Apollo's own examples check extensions.code.

Testing

  • npx vitest run: 27 files, 268 tests passed, none skipped, against a throwaway PostgreSQL 18 with the seeded catalog.
  • rejects a query that is too deep, and accepts one at the limit in server.test.ts fails on dev (INTERNAL_SERVER_ERROR instead of GRAPHQL_VALIDATION_FAILED) and passes with this change.
  • New errorCodes.test.ts runs the real server without a database: unknown field, syntax error, too deep, missing and invalid variable, unknown operationName, and an unexpected resolver error that is still hidden.
  • Turning the allowed list off makes 12 of the new tests fail, so they do catch the bug.
  • eslint backend/src/server and tsc --noEmit in backend pass.

Not done

  • docs/API.md has formatting differences from Prettier in the environment table (not from this PR); I left them alone to keep the diff small.

🤖 Generated with Claude Code

Since 84116e6 formatError hid every error that Apollo wraps in another
error. Apollo wraps its own errors too, so an unknown field, a syntax
error, a missing variable and an unknown operationName all came back as
INTERNAL_SERVER_ERROR, and the depth limit test failed on dev.

formatError now decides in three steps: our own errors keep their code,
errors with one of Apollo's client error codes (APOLLO_CLIENT_ERROR_CODES,
each listed by name) are let through, and everything else is still hidden
and logged. A test fails if Apollo adds a code that is not decided on.

The new tests run the real Apollo Server without a database, since the
old unit test used an error shape that Apollo does not send.
@ronjalh ronjalh requested a review from a team as a code owner October 22, 2026 14:06
@ronjalh ronjalh self-assigned this Oct 22, 2026
@anettkva anettkva merged commit 7fbafd1 into dev Oct 23, 2026
@anettkva anettkva deleted the 93-fix-invalid-queries-internal-error branch October 23, 2026 09:34
Sign in to join this conversation on GitHub.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants