{"record":{"id":"c924a933d2c62cac","repo":"mastra-ai/mastra","slug":"linear-api-error-body-errors-0-message-un","errorCode":null,"errorMessage":"Linear API error: ${body.errors[0]?.message ?? 'unknown error'}","messagePattern":"Linear API error: (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"mastracode/factory/src/integrations/linear/integration.ts","lineNumber":262,"sourceCode":"    body: JSON.stringify({ query, variables }),\n  });\n  if (!res.ok) {\n    // Linear returns GraphQL errors (validation, missing scopes, …) with a\n    // 400 status — surface the actual message instead of just the code.\n    let detail: string | null = null;\n    try {\n      const errBody = (await res.json()) as { errors?: Array<{ message?: string }> };\n      detail = errBody.errors?.[0]?.message ?? null;\n    } catch {\n      // Non-JSON error body; fall back to the status code alone.\n    }\n    const err = new Error(`Linear API request failed (${res.status})${detail ? `: ${detail}` : ''}`);\n    (err as { status?: number }).status = res.status;\n    throw err;\n  }\n  const body = (await res.json()) as { data?: T; errors?: Array<{ message?: string }> };\n  if (body.errors?.length) {\n    throw new Error(`Linear API error: ${body.errors[0]?.message ?? 'unknown error'}`);\n  }\n  if (!body.data) {\n    throw new Error('Linear API returned no data.');\n  }\n  return body.data;\n}\n\nexport class LinearIntegration implements FactoryIntegration {\n  /** Stable integration identifier (see `../base.ts`). */\n  readonly id = 'linear';\n  /** Bound once by the factory via `initialize()` before any surface is used. */\n  #storage: LinearStorageHandle | undefined;\n  #projects: FactoryProjectsStorage | undefined;\n  #auth: RouteAuth | undefined;\n\n  /** Bind Linear's slice of the generic integration storage, the projects domain, and the host auth seam. */\n  initialize({\n    storage,","sourceCodeStart":244,"sourceCodeEnd":280,"githubUrl":"https://github.com/mastra-ai/mastra/blob/75dd419e613fe9c39f846ffc500716141b74fda6/mastracode/factory/src/integrations/linear/integration.ts#L244-L280","documentation":"The `linearGraphql` helper throws this when Linear's GraphQL endpoint returns HTTP 200 but the response body contains a GraphQL `errors` array. Linear reports execution-level problems (validation errors, missing scopes, rate limits, bad UUIDs) this way even on 200 responses, and the integration surfaces the first error's `message` verbatim instead of the status code. It is a runtime API error from Linear's side, not a bug in your code.","triggerScenarios":"Any call routed through `linearGraphql` (e.g. `fetchWorkspace`, `listProjects`, `listActiveIssues`, `linear_create_comment`) where the response body has `errors[0].message` set: querying a non-existent issue UUID, using a token lacking the requested field's scope, malformed GraphQL query, or Linear-side rate limiting returned with a 200 status.","commonSituations":"Passing an issue id that is a display identifier (e.g. 'ENG-123') instead of a UUID; a connection granted only `read` scope attempting a comment mutation; Linear rate-limit errors after polling too aggressively; stale tokens revoked mid-flight.","solutions":["Read the message after the colon — it is Linear's own GraphQL error message and names the failing field/reason.","If the message mentions permissions or scope, reconnect Linear and request the needed scope (e.g. `comments:create` for posting).","If the message mentions an id or entity not found, verify you are passing Linear UUIDs, not human identifiers.","If it is a rate-limit error, back off and retry with fewer concurrent GraphQL calls.","If the message is 'unknown error', capture the full response body (e.g. via logging around `linearGraphql`) to see all `errors` entries."],"exampleFix":"// before: human-readable identifier\nawait integration.getIssue({ connection, externalId: 'ENG-123' });\n// after: Linear UUID from intake listItems metadata\nawait integration.getIssue({ connection, externalId: issue.externalId });","handlingStrategy":"try-catch","validationCode":"// Pre-flight: ensure a connection exists and has the needed scope before calling\nconst conn = await integration.loadConnection(orgId);\nif (!conn) throw new Error('Linear not connected');\nif (mutation && !integration.canPostComments(conn)) throw new Error('Linear scope comments:create missing');","typeGuard":"function isLinearGraphqlError(err: unknown): err is Error & { linearMessage: string } {\n  return err instanceof Error && err.message.startsWith('Linear API error: ');\n}","tryCatchPattern":"try {\n  const data = await linearGraphql<T>(token, query, variables);\n} catch (err) {\n  if (isLinearGraphqlError(err)) {\n    const detail = err.message.slice('Linear API error: '.length);\n    if (/permission|scope/i.test(detail)) promptReconnect();\n    else if (/not found/i.test(detail)) fixExternalId();\n    else if (/rate limit/i.test(detail)) scheduleRetryWithBackoff();\n  }\n  throw err;\n}","preventionTips":["Always use Linear UUIDs from intake metadata, never display identifiers like ENG-123.","Request all scopes you need (read, comments:create) in buildAuthorizeUrl.","Back off on rate-limit messages before retrying.","Log full response bodies for GraphQL failures to aid debugging."],"tags":["network","graphql","linear-api","third-party-api"],"backgroundTag":"graphql-errors-response","analyzedSha":"75dd419e613fe9c39f846ffc500716141b74fda6","analyzedAt":"2026-08-30T00:15:31.844Z","schemaVersion":2},"datasetVersion":"2026-08-30T03:17:51.788Z"}