Skip to content

fix(server): route middleware GraphQL errors through the error registry - #1540

Merged
pyramation merged 1 commit into
mainfrom
feat/graphql-error-messages
Jul 31, 2026
Merged

fix(server): route middleware GraphQL errors through the error registry#1540
pyramation merged 1 commit into
mainfrom
feat/graphql-error-messages

Conversation

@pyramation

Copy link
Copy Markdown
Contributor

Summary

A stale session token rendered in the Platform UI as a bare GraphQL Error: with nothing after it. The cause is not the error registry — @constructive-io/errors defines UNAUTHENTICATED correctly (public/401/"You must be signed in to do that.") and ConstructiveError.toExtensions() already produces a proper payload. The cause is that middleware/auth.ts bypassed the registry and hand-built the response body:

-res.status(200).json({ errors: [{ extensions: { code: 'UNAUTHENTICATED' } }] });
+respondWithGraphQLError(res, errors.UNAUTHENTICATED());

With no top-level message, every client that does errors.map(e => e.message).join('; ') renders an empty string.

The same file's catch branch was worse: it used an unregistered code (BAD_TOKEN_DEFINITION) and put the raw Postgres error text in extensions.message — invisible to clients and an internals leak. It now raises INTERNAL_FAILURE, with the database detail included only in development.

To stop this class of bug rather than the one instance, the response shape is now built in exactly one place:

// graphql/server/src/errors/graphql-response.ts
export function respondWithGraphQLError(res: Response, error: ConstructiveError): void {
  res.status(200).json({
    errors: [{ message: error.message, extensions: error.toExtensions() }],
  });
}

captcha.ts — the only other middleware hand-writing GraphQL error payloads — is routed through it too, which required registering CAPTCHA_REQUIRED and CAPTCHA_FAILED so no middleware needs string literals for codes.

Finally, defense on the client side: the generated ORM client falls back to extensions.code when a server omits message, so a message-less error can degrade to GraphQL Error: UNAUTHENTICATED instead of GraphQL Error:.

Testing

  • graphql/server: new errors/__tests__/graphql-response.test.ts (3 tests) asserting a top-level message, HTTP 200, and context interpolation; existing middleware suites 60/60.
  • graphql/codegen: new test constructing GraphQLRequestError from a message-less error and asserting the code fallback; suite 11/11 with the client snapshot updated.
  • tsc --noEmit clean for packages/errors, graphql/server, graphql/codegen.

Note: pnpm lint fails repo-wide before this change — ESLint 9 requires a flat config and the repo still ships .eslintrc.json. Unrelated to this diff, but worth fixing separately.

Link to Devin session: https://app.devin.ai/sessions/4bc483ff6d644a7b9a2fed56af9a2111
Requested by: @pyramation

A stale session rendered in the UI as a blank `GraphQL Error:` — the auth
middleware answered with `{errors:[{extensions:{code:'UNAUTHENTICATED'}}]}`
and no top-level `message`, which clients join into an empty string.

- auth/captcha middleware now emit registry-backed errors via a shared
  respondWithGraphQLError helper
- the auth failure path no longer returns the raw Postgres error text to
  clients (dev-only detail via INTERNAL_FAILURE)
- register CAPTCHA_REQUIRED / CAPTCHA_FAILED so no middleware needs literals
- generated ORM client falls back to extensions.code when message is absent
@pyramation pyramation self-assigned this Jul 31, 2026
@devin-ai-integration

Copy link
Copy Markdown
Contributor

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR. Add '(aside)' to your comment to have me ignore it.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment, CI, and merge conflict monitoring

@pyramation
pyramation merged commit 530bcd4 into main Jul 31, 2026
17 checks passed
@pyramation
pyramation deleted the feat/graphql-error-messages branch July 31, 2026 01:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant