From 2115b2335a9c7bdd17985177a8bd0eb83785bbcd Mon Sep 17 00:00:00 2001 From: Sonarly Claude Code Date: Fri, 6 Mar 2026 09:24:17 +0000 Subject: [PATCH] Unauthenticated GraphQL requests hit empty schema, producing "Unknown type" errors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit https://sonarly.com/issue/3956?type=bug When auth tokens expire/clear, the frontend sends GraphQL queries without authentication. The server returns an empty schema for unauthenticated requests, causing schema validation errors instead of a proper UNAUTHENTICATED error that the client can handle. Fix: ## Server fix — `graphql-config.service.ts` Instead of returning `new GraphQLSchema({})` when the workspace is not resolved (unauthenticated request), throw a `GraphQLError` with `extensions.code: 'UNAUTHENTICATED'`: ```typescript file=packages/twenty-server/src/engine/api/graphql/graphql-config/graphql-config.service.ts lines=87-99 conditionalSchema: async (context) => { const { workspace, user, application } = context.req; if (!isDefined(workspace)) { throw new GraphQLError('Unauthenticated', { extensions: { code: 'UNAUTHENTICATED', }, }); } try { return await this.createSchema(context, workspace, application?.id); ``` The check is moved **before** the `try` block so it propagates as a proper GraphQL error rather than being swallowed by the catch handler. The unused `GraphQLSchema` import was also removed. ## Monitoring noise fix (2a) — `apollo.factory.ts` Added a guard in the `default` case of the error link to skip Sentry when there is no token pair. Schema-validation errors (which have no `extensions.code`) that arrive when the user is unauthenticated are expected and non-actionable; sending them to Sentry is pure noise: ```typescript file=packages/twenty-front/src/modules/apollo/services/apollo.factory.ts lines=286-293 default: // Schema-validation errors (no extension code) caused by // unauthenticated requests are expected and non-actionable; // skip Sentry when there is no token pair. if (isUndefinedOrNull(getTokenPair())) { return; } sendToSentry({ graphQLError, operation }); ``` Together these two changes ensure: (1) unauthenticated requests get a proper `UNAUTHENTICATED` error the client can act on, (2) token renewal is triggered instead of broken object pages, and (3) as a defense-in-depth fallback, any residual uncodified GraphQL errors that arrive while unauthenticated are not forwarded to Sentry. --- .../src/modules/apollo/services/apollo.factory.ts | 6 ++++++ .../graphql-config/graphql-config.service.ts | 14 +++++++++----- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/packages/twenty-front/src/modules/apollo/services/apollo.factory.ts b/packages/twenty-front/src/modules/apollo/services/apollo.factory.ts index 5bb40542bfa..efbffa6a8ae 100644 --- a/packages/twenty-front/src/modules/apollo/services/apollo.factory.ts +++ b/packages/twenty-front/src/modules/apollo/services/apollo.factory.ts @@ -284,6 +284,12 @@ export class ApolloFactory implements ApolloManager { return; // already caught in BE } default: + // Schema-validation errors (no extension code) caused by + // unauthenticated requests are expected and non-actionable; + // skip Sentry when there is no token pair. + if (isUndefinedOrNull(getTokenPair())) { + return; + } sendToSentry({ graphQLError, operation }); } } diff --git a/packages/twenty-server/src/engine/api/graphql/graphql-config/graphql-config.service.ts b/packages/twenty-server/src/engine/api/graphql/graphql-config/graphql-config.service.ts index 6c7715a1836..8ea7dde6aa6 100644 --- a/packages/twenty-server/src/engine/api/graphql/graphql-config/graphql-config.service.ts +++ b/packages/twenty-server/src/engine/api/graphql/graphql-config/graphql-config.service.ts @@ -7,7 +7,7 @@ import { type YogaDriverServerContext, } from '@graphql-yoga/nestjs'; import * as Sentry from '@sentry/node'; -import { GraphQLError, GraphQLSchema } from 'graphql'; +import { GraphQLError } from 'graphql'; import GraphQLJSON from 'graphql-type-json'; import { type GraphQLSchemaWithContext, @@ -87,11 +87,15 @@ export class GraphQLConfigService conditionalSchema: async (context) => { const { workspace, user, application } = context.req; - try { - if (!isDefined(workspace)) { - return new GraphQLSchema({}); - } + if (!isDefined(workspace)) { + throw new GraphQLError('Unauthenticated', { + extensions: { + code: 'UNAUTHENTICATED', + }, + }); + } + try { return await this.createSchema(context, workspace, application?.id); } catch (error) { if (error instanceof UnauthorizedException) {