eyaltoledano/claude-task-master · error · AuthenticationError
FLOW_NOT_FOUND
FLOW_NOT_FOUND
Error message
Authentication flow expired or not found
What it means
AuthenticationError thrown during polling when the auth server responds with HTTP 404 for the flow-status endpoint, meaning the authentication flow ID is unknown or has expired server-side. The library treats this as terminal — polling will not succeed by continuing, so it aborts immediately.
Source
Thrown at packages/tm-core/src/modules/auth/services/oauth-service.ts:339
);
}
while (Date.now() - startTime < timeout) {
try {
const response = await fetch(statusUrl, {
method: 'GET',
headers: {
'User-Agent': `TaskMasterCLI/${this.getCliVersion()}`
}
});
if (!response.ok) {
const errorData = (await response.json().catch(() => ({}))) as {
message?: string;
};
if (response.status === 404) {
throw new AuthenticationError(
'Authentication flow expired or not found',
'FLOW_NOT_FOUND'
);
}
throw new AuthenticationError(
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'
);View on GitHub (pinned to c0c98d367c)
Solutions
- Restart the entire authentication flow (start a new PKCE flow) — the old flowId cannot be recovered.
- Complete the browser authorization step promptly, within the server's flow TTL.
- If self-hosting behind multiple instances, enable shared/sticky storage for CLI auth flows.
- Verify baseUrl points to the same environment that issued the flowId (dev vs prod mismatch).
- Check server logs for flow_id to confirm whether it expired or was never created.
Example fix
// before: retrying a dead flow
await oauth.pollForCompletion(oldFlowId, 60000); // 404 -> FLOW_NOT_FOUND
// after: catch and restart the flow
try {
await oauth.pollForCompletion(flowId, 60000);
} catch (e) {
if (e.code === 'FLOW_NOT_FOUND') {
const flow = await oauth.startBackendFlow();
await oauth.pollForCompletion(flow.flowId, 60000);
}
} Defensive patterns
Strategy: try-catch
Validate before calling
// Cannot be predicted client-side; mitigate by completing authorization promptly.
// Optionally verify the flow is fresh before polling:
if (Date.now() - flow.startedAt > 5 * 60 * 1000) {
flow = await oauth.startBackendFlow(); // flow likely expired server-side
} Type guard
function isFlowNotFound(e: unknown): e is AuthenticationError {
return e instanceof AuthenticationError && (e as any).code === 'FLOW_NOT_FOUND';
} Try / catch
try {
await oauth.pollForCompletion(flowId, timeout);
} catch (e) {
if (isFlowNotFound(e)) {
// Flow expired or server restarted — start a brand-new login flow
const fresh = await oauth.startBackendFlow();
await oauth.pollForCompletion(fresh.flowId, timeout);
}
} Prevention
- Complete the browser authorization step quickly, within the server's flow TTL
- Restart the whole flow instead of retrying an expired flowId
- Ensure sticky/shared flow storage when the backend runs multiple instances
- Point the CLI at the same environment (dev/prod) that issued the flowId
When it happens
Trigger: pollForCompletion receives a 404 from /api/auth/cli/status?flow_id=... because the flow timed out on the server, the server was restarted (in-memory flow store), a load balancer routed the poll to a different instance, or the flowId is wrong/already consumed.
Common situations: User taking longer than the server-side flow TTL to authorize in the browser; multi-instance backend without shared session storage; re-running a poll after a completed/expired login; pointing at a different environment's auth server than the one that issued the flowId.
Understand the failure class
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
AI-assisted analysis of eyaltoledano/claude-task-master@c0c98d367c (2026-08-29).
Data as JSON: /api/errors/5139bdb4197bf440.
Report an issue: GitHub.