mastra-ai/mastra · error
Linear ${label} returned no access token.
Error message
Linear ${label} returned no access token. What it means
`#requestTokens` POSTs to Linear's OAuth token endpoint for either a code exchange or a refresh. If the endpoint returns HTTP 200 but the JSON body has no `access_token`, it throws this error naming the grant type ('token exchange' or 'token refresh'). A 200 without an access token violates the OAuth spec and indicates Linear returned an unexpected payload.
Source
Thrown at mastracode/factory/src/integrations/linear/integration.ts:645
body: new URLSearchParams({
...params,
client_id: this.#clientId,
client_secret: this.#clientSecret,
}),
});
if (!res.ok) {
const err = new Error(`Linear ${label} failed (${res.status})`);
(err as { status?: number }).status = res.status;
throw err;
}
const body = (await res.json()) as {
access_token?: string;
refresh_token?: string;
expires_in?: number;
scope?: string;
};
if (!body.access_token) {
throw new Error(`Linear ${label} returned no access token.`);
}
return {
accessToken: body.access_token,
refreshToken: body.refresh_token ?? null,
expiresAt: typeof body.expires_in === 'number' ? new Date(Date.now() + body.expires_in * 1000) : null,
scope: body.scope ?? null,
};
}
// ── GraphQL reads/writes ─────────────────────────────────────────────────
/** Fetch the workspace (organization) the access token is scoped to. */
async fetchWorkspace(accessToken: string): Promise<LinearWorkspace> {
const data = await linearGraphql<{ organization: { name: string; urlKey: string } }>(
accessToken,
`query { organization { name urlKey } }`,
);
return { name: data.organization.name, urlKey: data.organization.urlKey };View on GitHub (pinned to 75dd419e61)
Solutions
- Retry the grant once — for `refresh_token` grants note Linear rotates tokens, so re-read the stored refresh token first (the integration already reloads before refreshing).
- For 'token exchange', restart the OAuth flow: authorization codes are single-use, so a new code via buildAuthorizeUrl.
- Log/inspect the raw 200 body to see what was actually returned.
- If mocking the token endpoint, make fixtures include `access_token` (plus `refresh_token`, `expires_in`, `scope`).
- Check for proxies or middlewares between your server and LINEAR_TOKEN_URL.
Example fix
// test mock before
mockTokenEndpoint = () => ({ status: 200, body: {} });
// after
mockTokenEndpoint = () => ({ status: 200, body: { access_token: 'ltok', refresh_token: 'rtok', expires_in: 7200, scope: 'read comments:create' } }); Defensive patterns
Strategy: retry
Type guard
function isMissingAccessTokenError(err: unknown): boolean {
return err instanceof Error && err.message.includes('returned no access token.');
} Try / catch
try {
const tokens = await integration.exchangeOAuthCode(code, redirectUri);
} catch (err) {
if (isMissingAccessTokenError(err) && err.message.includes('token exchange')) {
// Authorization codes are single-use: restart the OAuth flow with a fresh code.
restartOAuthFlow();
return;
}
throw err;
} Prevention
- Make token-endpoint test mocks include access_token, refresh_token, expires_in, and scope.
- Restart the OAuth flow rather than retrying an exchange — codes are single-use.
- For refresh grants, always reload the stored refresh token before retrying (Linear rotates it).
- Inspect raw 200 bodies when this fires to spot proxy interference.
When it happens
Trigger: `exchangeOAuthCode` or `refreshAccessToken` receiving a 200 response whose body lacks `access_token` — e.g. an intermediary/proxy returning a 200 with an empty or non-OAuth JSON body, a mocked token endpoint with incomplete fixtures, or a Linear-side anomaly.
Common situations: Test mocks that return 200 with `{}`; gateway/proxy rewrites stripping the body; using an incorrect token URL override pointing at something that answers 200; network appliances returning cached empty responses.
Related errors
- Token exchange failed: ${error}
- Failed to fetch user info from Auth0
- Token exchange failed: ${error}
- Failed to fetch user info from Clerk
- Google token exchange failed: ${error}
AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30).
Data as JSON: /api/errors/ed957fa51f174e17.
Report an issue: GitHub.