twentyhq/twenty · error · Error

Server error (${response.status})

Error message

Server error (${response.status})

What it means

callAppRoute POSTs JSON to a Twenty app route (TWENTY_FUNCTIONS_URL or TWENTY_API_URL/s<path>) and, on a non-ok response, extracts a message. 'Server error (status)' is the FALLBACK string used only when the response body is not parseable JSON or the parsed JSON lacks any of messages/message/error. So this exact text means the server returned an error status with an unstructured (often non-JSON) body.

Source

Thrown at packages/twenty-apps/internal/twenty-partners/src/modules/shared/front-components/call-app-route.ts:40

): Promise<unknown> => {
  const token = process.env.TWENTY_APP_ACCESS_TOKEN;

  const functionsUrl = process.env.TWENTY_FUNCTIONS_URL;
  const apiUrl = process.env.TWENTY_API_URL ?? '';
  const normalizedPath = path.startsWith('/') ? path : `/${path}`;
  const url = functionsUrl ? `${functionsUrl}${normalizedPath}` : `${apiUrl}/s${normalizedPath}`;

  const response = await fetch(url, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      ...(token ? { Authorization: `Bearer ${token}` } : {}),
    },
    body: JSON.stringify(body),
  });

  if (!response.ok) {
    throw new Error(await extractErrorMessage(response));
  }

  return response.json();
};

View on GitHub (pinned to 1f5dd2bbd2)

Solutions

  1. Verify TWENTY_FUNCTIONS_URL / TWENTY_API_URL / TWENTY_APP_ACCESS_TOKEN are set and the URL resolves to the Twenty workspace.
  2. Confirm the target app route is deployed and registered at the given path.
  3. Reproduce the POST with curl -i to see the raw status and body (HTML vs JSON).
  4. If the body is HTML from a gateway, check the functions/hosting platform health.

Example fix

// before
if (!response.ok) {
  throw new Error(await extractErrorMessage(response));
}

// after — include the URL and status code in the fallback so the cause is diagnosable
if (!response.ok) {
  const message = await extractErrorMessage(response);
  throw new Error(`${message} (url=${url}, status=${response.status})`);
}
Defensive patterns

Strategy: try-catch

Validate before calling

function assertCallAppRouteEnv() {
  const functionsUrl = process.env.TWENTY_FUNCTIONS_URL;
  const apiUrl = process.env.TWENTY_API_URL;
  if (!functionsUrl && !apiUrl) {
    throw new Error('Set TWENTY_FUNCTIONS_URL or TWENTY_API_URL before calling app routes');
  }
  if (!process.env.TWENTY_APP_ACCESS_TOKEN) {
    throw new Error('Set TWENTY_APP_ACCESS_TOKEN before calling app routes');
  }
}
await assertCallAppRouteEnv();

Try / catch

try {
  return await callAppRoute('/my-route', body);
} catch (err) {
  // 'Server error (status)' means the body was non-JSON or lacked an error field.
  // Reproduce with curl -i against the same URL/token to see the raw body.
  throw new Error(`callAppRoute failed: ${err instanceof Error ? err.message : String(err)}`);
}

Prevention

When it happens

Trigger: response.ok is false AND extractErrorMessage could not find a structured error field. Happens when a gateway/proxy returns HTML (502/503/504), the app route threw an unhandled error producing a plain-text body, the URL is wrong (404 HTML page), or TWENTY_APP_ACCESS_TOKEN is missing/expired and the server returns a non-JSON auth error.

Common situations: TWENTY_FUNCTIONS_URL or TWENTY_API_URL misconfigured (trailing slash, wrong host) hitting a 404 HTML page; functions deployment down (502 from platform); token unset so auth middleware returns a non-JSON 401; app route not deployed/registered.

Related errors


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