twentyhq/twenty · error · Error

${response.statusText}: ${response.rawBody}

Error message

${response.statusText}: ${response.rawBody}

What it means

Thrown by TwentyClient.assertResponseIsSuccessful (twenty-client-template.ts:375-378) when the GraphQL HTTP response status is outside the 200-2999 range. This runs after token-refresh retry logic, so the error reflects a final, unrecoverable HTTP-level failure. The message combines statusText and the raw (unparsed) response body.

Source

Thrown at packages/twenty-client-sdk/src/generate/twenty-client-template.ts:377

  private async resolveHeaders(): Promise<HeadersInit> {
    if (typeof this.headers === 'function') {
      return (await this.headers()) ?? {};
    }

    return this.headers ?? {};
  }

  private shouldRefreshToken(response: GraphqlResponse): boolean {
    if (response.status === 401) {
      return true;
    }

    return hasAuthenticationErrorInGraphqlPayload(response.payload);
  }

  private assertResponseIsSuccessful(response: GraphqlResponse) {
    if (response.status < 200 || response.status >= 300) {
      throw new Error(`${response.statusText}: ${response.rawBody}`);
    }

    if (response.payload === null) {
      throw new Error('Invalid JSON response');
    }

    return response.payload;
  }

  private async requestRefreshedAccessToken(): Promise<string | null> {
    const refreshAccessTokenFunction = (
      globalThis as {
        frontComponentHostCommunicationApi?: {
          requestAccessTokenRefresh?: () => Promise<string>;
        };
      }
    ).frontComponentHostCommunicationApi?.requestAccessTokenRefresh;

View on GitHub (pinned to 1f5dd2bbd2)

Solutions

  1. Read the rawBody portion of the message — it contains the server's actual error payload.
  2. For 401s, register a token refresh hook on globalThis.frontComponentHostCommunicationApi.requestAccessTokenRefresh or supply a fresh token.
  3. Verify apiUrl correctness and that the Twenty server is up.
  4. Check CORS configuration if running in a browser (opaque responses get status 0).

Example fix

// before — no refresh hook, 401 surfaces as error
// after — register a refresh hook before constructing the client
(globalThis as any).frontComponentHostCommunicationApi = {
  requestAccessTokenRefresh: async () => refreshTokenPair(),
};
Defensive patterns

Strategy: try-catch

Try / catch

try {
  const result = await client.someQuery();
} catch (err) {
  // message is `${statusText}: ${rawBody}`
  const colonIdx = err.message.indexOf(':');
  const statusText = err.message.slice(0, colonIdx);
  const rawBody = err.message.slice(colonIdx + 2);
  if (statusText === 'Unauthorized') {
    await refreshAccessToken();
  }
}

Prevention

When it happens

Trigger: Any GraphQL request whose HTTP status is < 200 or >= 300 after the optional 401 refresh-and-retry path. Common codes: 401 (refresh failed or no refresh hook), 403, 404, 500, 502, 503.

Common situations: Expired access token with no frontComponentHostCommunicationApi.requestAccessTokenRefresh hook registered; server-side 500 from a malformed operation; reverse proxy returning 502/504; wrong apiUrl pointing to a non-GraphQL route returning 404; CORS errors producing an opaque 0-status response.

Related errors


AI-assisted analysis of twentyhq/twenty@1f5dd2bbd2 (2026-08-12). Data as JSON: /api/errors/9be76eb2e5fd292d. Report an issue: GitHub.