eyaltoledano/claude-task-master · error · AuthenticationError
MISSING_TOKENS
MISSING_TOKENS
Error message
'Server returned no encrypted tokens'
What it means
MISSING_TOKENS is thrown when the poll reports status 'complete' but the response body lacks the `encrypted_tokens` field. The CLI decrypts tokens locally with an ephemeral keypair, so a 'complete' result without the encrypted payload is a server-side contract violation and cannot be recovered.
Source
Thrown at packages/tm-core/src/modules/auth/services/oauth-service.ts:364
errorData.message || `HTTP ${response.status}`,
'POLL_FAILED'
);
}
const data = (await response.json()) as FlowStatusResponse;
if (!data.success) {
throw new AuthenticationError(
data.message || 'Failed to check status',
'POLL_FAILED'
);
}
switch (data.status) {
case 'complete': {
// Decrypt tokens using our private key
if (!data.encrypted_tokens) {
throw new AuthenticationError(
'Server returned no encrypted tokens',
'MISSING_TOKENS'
);
}
const tokens = decryptTokens(
data.encrypted_tokens,
this.keyPair.privateKey
);
this.logger.debug('Successfully decrypted authentication tokens');
return {
token: tokens.access_token,
refreshToken: tokens.refresh_token,
userId: tokens.user_id,
email: tokens.email,
expiresAt: tokens.expires_inView on GitHub (pinned to c0c98d367c)
Solutions
- Retry `credentials()` to start a fresh flow with a new keypair — a transient server glitch is the most common cause.
- Check that CLI and backend versions match (the status response schema may have changed); update @tm/core / the CLI.
- Verify no proxy/middleware strips or truncates the response body (encrypted_tokens can be large).
- Report persistent occurrences with the flow_id to the backend team — 'complete' without tokens is a server bug.
Example fix
// before: assumes complete always carries tokens, hard failure
const creds = await oauthService.credentials();
// after: handle MISSING_TOKENS with one retry
try {
return await oauthService.credentials();
} catch (e) {
if (e instanceof AuthenticationError && e.code === 'MISSING_TOKENS') {
return oauthService.credentials(); // fresh flow regenerates keypair
}
throw e;
} Defensive patterns
Strategy: retry
Type guard
function isCompleteWithTokens(d: FlowStatusResponse):
d is FlowStatusResponse & { status: 'complete'; encrypted_tokens: string } {
return d.status === 'complete' && typeof d.encrypted_tokens === 'string' && d.encrypted_tokens.length > 0;
} Try / catch
try {
return await oauthService.credentials();
} catch (e) {
if (e instanceof AuthenticationError && e.code === 'MISSING_TOKENS') {
// fresh flow regenerates keypair and asks server for tokens again
return oauthService.credentials();
}
throw e;
} Prevention
- Keep CLI/tm-core and backend deployments version-aligned.
- Don't route auth API responses through response-transforming middleware.
- Retry once automatically — a fresh flow usually resolves transient glitches.
- Report persistent cases to the backend team with the flow_id.
When it happens
Trigger: `credentials()` polling receives `{success:true,status:'complete'}` but `data.encrypted_tokens` is undefined/null — the backend marked the flow complete without attaching the RSA-encrypted token payload.
Common situations: Backend version mismatch where the status endpoint no longer includes encrypted_tokens; a reverse proxy or response-transforming middleware stripping large fields; server bug when storing flow results; client pointed at an older API deployment.
Related errors
AI-assisted analysis of eyaltoledano/claude-task-master@c0c98d367c (2026-08-29).
Data as JSON: /api/errors/147f033f7cb3bcfe.
Report an issue: GitHub.