eyaltoledano/claude-task-master · error · AuthenticationError

START_FLOW_FAILED

START_FLOW_FAILED

Error message

startResponse.message || 'Failed to start authentication flow'

What it means

During authenticateWithBackendPKCE the client generates an RSA keypair, then asks the backend to start a PKCE auth flow (startBackendFlow). If the backend response is unsuccessful or lacks a flow_id, the client throws AuthenticationError with code START_FLOW_FAILED, using the backend's message when provided.

Source

Thrown at packages/tm-core/src/modules/auth/services/oauth-service.ts:162

		options: OAuthFlowOptions
	): Promise<AuthCredentials> {
		const {
			openBrowser,
			timeout = 300000,
			onAuthUrl,
			onWaitingForAuth,
			onSuccess
		} = options;

		// Step 1: Generate keypair for E2E encryption
		this.keyPair = generateKeyPair();
		this.logger.debug('Generated RSA keypair for E2E encryption');

		// Step 2: Start the flow on the backend with our public key
		const startResponse = await this.startBackendFlow();

		if (!startResponse.success || !startResponse.flow_id) {
			throw new AuthenticationError(
				startResponse.message || 'Failed to start authentication flow',
				'START_FLOW_FAILED'
			);
		}

		const { flow_id, verification_url, poll_interval = 2 } = startResponse;

		// Store the auth URL
		this.authorizationUrl = verification_url || null;

		// Notify about the auth URL
		if (onAuthUrl && verification_url) {
			onAuthUrl(verification_url);
		}

		// Step 3: Open browser with verification URL
		if (openBrowser && verification_url) {
			try {

View on GitHub (pinned to c0c98d367c)

Solutions

  1. Verify the backend base URL/configuration is correct and the server is reachable (curl the /api/auth/cli/start endpoint)
  2. Check the thrown message for backend-provided details and follow them
  3. Retry after a short delay if it was transient (5xx/network); check backend service health
  4. Update the CLI if the backend auth API version changed
  5. Catch AuthenticationError code START_FLOW_FAILED and surface startResponse.message to the user

Example fix

// before
await auth.authenticate(); // opaque START_FLOW_FAILED
// after
try {
  await auth.authenticate();
} catch (e) {
  if (e instanceof AuthenticationError && e.code === 'START_FLOW_FAILED') {
    console.error('Auth backend rejected flow start:', e.message);
  }
  throw e;
}
Defensive patterns

Strategy: retry

Validate before calling

// preflight: confirm the auth backend is reachable
const res = await fetch(`${baseUrl}/api/auth/cli/start`, { method: 'HEAD' }).catch(() => null);
if (!res) throw new Error('Auth backend unreachable at ' + baseUrl);

Type guard

function isStartFlowFailed(e: unknown): e is AuthenticationError {
  return e instanceof AuthenticationError && e.code === 'START_FLOW_FAILED';
}

Try / catch

for (let i = 0; i < 3; i++) {
  try { await auth.authenticate(); break; }
  catch (e) {
    if (isStartFlowFailed(e) && i < 2) { await sleep(2000 * (i + 1)); continue; }
    throw e;
  }
}

Prevention

When it happens

Trigger: POST to `${baseUrl}/api/auth/cli/start` returns success=false, an error payload, or a 2xx body without flow_id — e.g. backend outage, unsupported server version, rate limiting, or wrong base URL hitting an unexpected endpoint.

Common situations: Pointing tm at a stale/mismatched backend URL; API server down or deployed with a different auth API; corporate proxy intercepting the request; backend rate-limits repeated login attempts.

Understand the failure class

Related errors


AI-assisted analysis of eyaltoledano/claude-task-master@c0c98d367c (2026-08-29). Data as JSON: /api/errors/a994fad48af82476. Report an issue: GitHub.