{"record":{"id":"69ab835e7763592e","repo":"twentyhq/twenty","slug":"caller-failed-http-response-status-resp","errorCode":null,"errorMessage":"${caller}() failed: HTTP ${response.status} ${response.statusText}","messagePattern":"(.+?)\\(\\) failed: HTTP (.+?) (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/twenty-sdk/src/sdk/logic-function/utils/post-graphql-request.util.ts","lineNumber":35,"sourceCode":"\n  if (!apiUrl || !accessToken) {\n    throw new Error(\n      `${caller}() requires the app runtime env vars ` +\n        `${DEFAULT_API_URL_NAME} and ${DEFAULT_APP_ACCESS_TOKEN_NAME}.`,\n    );\n  }\n\n  const response = await fetch(`${apiUrl}/metadata`, {\n    method: 'POST',\n    headers: {\n      'Content-Type': 'application/json',\n      Authorization: `Bearer ${accessToken}`,\n    },\n    body: JSON.stringify({ query, variables }),\n  });\n\n  if (!response.ok) {\n    throw new Error(\n      `${caller}() failed: HTTP ${response.status} ${response.statusText}`,\n    );\n  }\n\n  const body = (await response.json()) as {\n    data?: TData;\n    errors?: { message: string }[];\n  };\n\n  if (body.errors && body.errors.length > 0) {\n    throw new Error(\n      `${caller}() failed: ${body.errors.map((error) => error.message).join(', ')}`,\n    );\n  }\n\n  if (!body.data) {\n    throw new Error(`${caller}() failed: response contained no data.`);\n  }","sourceCodeStart":17,"sourceCodeEnd":53,"githubUrl":"https://github.com/twentyhq/twenty/blob/1f5dd2bbd2a8da3419c8cfd52dd545c0024df1a6/packages/twenty-sdk/src/sdk/logic-function/utils/post-graphql-request.util.ts#L17-L53","documentation":"Thrown by postGraphqlRequest when the HTTP response from the Twenty metadata endpoint has a non-2xx status code. This means the transport-level request failed before GraphQL processing — the server rejected the request at the HTTP layer. The status code and status text are included for diagnosis.","triggerScenarios":"The fetch to {apiUrl}/metadata returns a non-ok status: 401 (token expired/invalid), 403 (forbidden), 404 (wrong URL/path), 500 (server error), 502/503 (gateway/service unavailable). This is distinct from GraphQL-level errors (error 98) which come back as 200 with an errors array.","commonSituations":"The app access token expired and needs refreshing. The TWENTY_API_URL points to the wrong path (e.g. missing /metadata suffix is handled by code, but wrong base URL). The Twenty server is temporarily down. CORS or network proxy issues in browser environments. Rate limiting returns 429.","solutions":["If status is 401: refresh the access token via the SDK CLI or token refresh flow, then retry.","If status is 404: verify TWENTY_API_URL is correct and the /metadata endpoint exists on that server.","If status is 500/502/503: the server is down — retry with exponential backoff.","If status is 429: you are being rate limited — reduce request frequency and retry after a delay.","Check for network/proxy issues if the status text indicates a connection problem."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n  const data = await postGraphqlRequest({ query, variables, caller: 'myAction' });\n} catch (error) {\n  if (error instanceof Error && error.message.includes('HTTP')) {\n    const status = error.message.match(/HTTP (\\d+)/)?.[1];\n    if (status === '401') await refreshTokenAndRetry();\n    else if (status?.startsWith('5')) await retryWithBackoff();\n    else throw error;\n    return;\n  }\n  throw error;\n}","preventionTips":["Implement token refresh logic for 401 responses.","Use exponential backoff retry for 5xx errors.","Monitor HTTP error rates to detect server outages or misconfigured URLs."],"tags":["sdk","graphql","network","http-error","authentication"],"backgroundTag":null,"analyzedSha":"1f5dd2bbd2a8da3419c8cfd52dd545c0024df1a6","analyzedAt":"2026-08-12T15:37:27.593Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}