mastra-ai/mastra · error · Error
Linear API error: ${body.errors[0]?.message ?? 'unknown erro
Error message
Linear API error: ${body.errors[0]?.message ?? 'unknown error'} What it means
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.
Source
Thrown at mastracode/factory/src/integrations/linear/integration.ts:262
body: JSON.stringify({ query, variables }),
});
if (!res.ok) {
// Linear returns GraphQL errors (validation, missing scopes, …) with a
// 400 status — surface the actual message instead of just the code.
let detail: string | null = null;
try {
const errBody = (await res.json()) as { errors?: Array<{ message?: string }> };
detail = errBody.errors?.[0]?.message ?? null;
} catch {
// Non-JSON error body; fall back to the status code alone.
}
const err = new Error(`Linear API request failed (${res.status})${detail ? `: ${detail}` : ''}`);
(err as { status?: number }).status = res.status;
throw err;
}
const body = (await res.json()) as { data?: T; errors?: Array<{ message?: string }> };
if (body.errors?.length) {
throw new Error(`Linear API error: ${body.errors[0]?.message ?? 'unknown error'}`);
}
if (!body.data) {
throw new Error('Linear API returned no data.');
}
return body.data;
}
export class LinearIntegration implements FactoryIntegration {
/** Stable integration identifier (see `../base.ts`). */
readonly id = 'linear';
/** Bound once by the factory via `initialize()` before any surface is used. */
#storage: LinearStorageHandle | undefined;
#projects: FactoryProjectsStorage | undefined;
#auth: RouteAuth | undefined;
/** Bind Linear's slice of the generic integration storage, the projects domain, and the host auth seam. */
initialize({
storage,View on GitHub (pinned to 75dd419e61)
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.
Example fix
// before: human-readable identifier
await integration.getIssue({ connection, externalId: 'ENG-123' });
// after: Linear UUID from intake listItems metadata
await integration.getIssue({ connection, externalId: issue.externalId }); Defensive patterns
Strategy: try-catch
Validate before calling
// Pre-flight: ensure a connection exists and has the needed scope before calling
const conn = await integration.loadConnection(orgId);
if (!conn) throw new Error('Linear not connected');
if (mutation && !integration.canPostComments(conn)) throw new Error('Linear scope comments:create missing'); Type guard
function isLinearGraphqlError(err: unknown): err is Error & { linearMessage: string } {
return err instanceof Error && err.message.startsWith('Linear API error: ');
} Try / catch
try {
const data = await linearGraphql<T>(token, query, variables);
} catch (err) {
if (isLinearGraphqlError(err)) {
const detail = err.message.slice('Linear API error: '.length);
if (/permission|scope/i.test(detail)) promptReconnect();
else if (/not found/i.test(detail)) fixExternalId();
else if (/rate limit/i.test(detail)) scheduleRetryWithBackoff();
}
throw err;
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- Linear API returned no data.
- Token exchange failed: ${error}
- Failed to fetch user info from Auth0
- Token exchange failed: ${error}
- Failed to fetch user info from Clerk
AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30).
Data as JSON: /api/errors/c924a933d2c62cac.
Report an issue: GitHub.