Skip to content

fix: fall through to OAuth when CLI credentials are expired#386

Merged
Th0rgal merged 1 commit intomasterfrom
fix/expired-cli-credentials
Apr 14, 2026
Merged

fix: fall through to OAuth when CLI credentials are expired#386
Th0rgal merged 1 commit intomasterfrom
fix/expired-cli-credentials

Conversation

@Th0rgal
Copy link
Copy Markdown
Owner

@Th0rgal Th0rgal commented Apr 14, 2026

Summary

  • When a mission workspace has a .credentials.json from a previous run but the token has expired, the system would still trust it (has_cli_creds=true) and skip OAuth injection
  • Claude Code would then use the expired token and fail with "Invalid authentication credentials"
  • Now checks token expiry after the copy/refresh attempt; if expired, removes the stale file and falls through to the OAuth injection path

Root cause

The needs_copy logic correctly detects expired tokens and tries to re-copy from host credentials. But when no host credentials exist (common when auth is managed via OAuth providers, not claude login), the copy is skipped and the stale expired token persists. The has_cli_creds flag remains true because the file exists, so the OAuth path is never tried.

Test plan

  • Verified on prod server: mission 9b413dc4 had this exact failure pattern
  • Already deployed the earlier bun-symlink fix; this auth fix needs separate deploy
  • Create a mission with an expired credentials file and verify it falls through to OAuth

Note

Medium Risk
Touches credential-selection logic for missions and adds deletion of expired .credentials.json, which could affect authentication behavior if expiry detection is wrong or filesystem operations fail in edge cases.

Overview
Prevents missions from trusting stale Claude CLI credentials: after any copy/refresh attempt, the runner now checks expires_at from the mission .credentials.json, logs an is_expired flag, and if expired forces has_cli_creds=false.

When expired credentials are detected, it removes the stale credentials file so Claude Code won’t pick it up, allowing the flow to fall through to the existing OAuth token injection/refresh path instead of failing with invalid credentials.

Reviewed by Cursor Bugbot for commit d9940c2. Bugbot is set up for automated code reviews on this repo. Configure here.

When a mission workspace has a .credentials.json from a previous run
but the token has expired, the system would still trust it and skip
OAuth injection. Claude Code would then fail with "Invalid
authentication credentials".

Now checks expiry after the copy/refresh attempt. If the token is
expired, removes the stale file and sets has_cli_creds=false so the
OAuth injection path runs instead.
@vercel
Copy link
Copy Markdown

vercel bot commented Apr 14, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
sandboxed-dashboard Ready Ready Preview, Comment Apr 14, 2026 0:59am
sandboxed-sh Ready Ready Preview, Comment Apr 14, 2026 0:59am

Request Review

Copy link
Copy Markdown

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit d9940c2. Configure here.

Comment thread src/api/mission_runner.rs
let mut has_cli_creds = looks_like_claude_cli_credentials(&mission_creds_path);
if let Some((expires_at, has_refresh)) = claude_cli_credentials_info(&mission_creds_path) {
let now_ms = chrono::Utc::now().timestamp_millis();
let is_expired = expires_at < now_ms;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Inconsistent expiry buffer leaves near-expiry file undeleted

Medium Severity

The expiry check at is_expired = expires_at < now_ms uses no buffer, but looks_like_claude_cli_credentials uses a 60-second buffer (expires_at < now_ms + 60_000). When a token is within 0–60 seconds of expiry, has_cli_creds becomes false (falls through to OAuth), but is_expired is false so the stale credentials file is not removed. Claude Code can still discover and use the near-expiry file, defeating the fallthrough to OAuth. The is_expired check needs the same 60-second buffer to ensure file removal is consistent with the has_cli_creds decision.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit d9940c2. Configure here.

@Th0rgal Th0rgal merged commit e61ada6 into master Apr 14, 2026
10 checks passed
@Th0rgal Th0rgal deleted the fix/expired-cli-credentials branch April 14, 2026 13:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant