OAuth token expires after inactivity causing 'Token refresh failed: 400' #6616

Closed
opened 2026-02-16 18:04:46 -05:00 by yindo · 2 comments
Owner

Originally created by @mguttmann on GitHub (Jan 17, 2026).

Originally assigned to: @thdxr on GitHub.

Problem

Users report that after several hours of inactivity (especially after sleep/overnight), the Anthropic OAuth token expires and the refresh fails with a 400 error:

Error: Token refresh failed: 400

This forces users to re-login every morning or after periods of inactivity.

Related Issues:

  • #6559 - Claude subscription token expires after a period of time
  • #4992 - Getting "Unauthorized: token expired" during conversation

Root Cause

Anthropic's OAuth refresh tokens appear to have a limited lifetime. When the access token expires and OpenCode attempts to refresh it, the refresh token itself may have already been invalidated by Anthropic's servers after extended periods of inactivity.

The current implementation only refreshes tokens reactively (when a request fails), rather than proactively keeping them alive.

Proposed Solution

Implement a token keep-alive mechanism that periodically pings all logged-in Anthropic OAuth accounts to prevent token expiration:

  1. Use the existing /api/oauth/usage endpoint - This endpoint:

    • Already exists in the codebase (Auth.OAuthPool.fetchAnthropicUsage)
    • Requires authentication (keeps the token active)
    • Does NOT consume any tokens or context
    • Returns usage statistics (bonus: could be useful for UI)
  2. Ping interval: Every 60 minutes

    • First ping after 5 minutes (to let everything settle after startup)
    • Then every hour while OpenCode is running
  3. Multi-account support: Ping ALL logged-in Anthropic OAuth accounts

    • Users with multiple accounts (Account 1, 2, 3, etc.) will have all accounts kept alive
    • Only affects Anthropic provider (other providers like Copilot, OpenAI are not affected)

Implementation

New module: packages/opencode/src/auth/keepalive.ts

// Pings all Anthropic OAuth accounts every hour
// Uses /api/oauth/usage endpoint (no token cost)
// Logs success/failure for debugging

Integrated into InstanceBootstrap() so it starts automatically when OpenCode runs.

Benefits

  • No more morning re-logins - Tokens stay fresh as long as OpenCode is running
  • Zero cost - Uses usage endpoint, no tokens consumed
  • Multi-account friendly - All accounts stay active
  • Non-intrusive - Runs in background, logged for debugging
  • Automatic - No user action required

Testing

  • Run OpenCode for extended periods
  • Monitor logs for auth.keepalive messages
  • Verify tokens remain valid after previously problematic timeframes
Originally created by @mguttmann on GitHub (Jan 17, 2026). Originally assigned to: @thdxr on GitHub. ## Problem Users report that after several hours of inactivity (especially after sleep/overnight), the Anthropic OAuth token expires and the refresh fails with a `400` error: ``` Error: Token refresh failed: 400 ``` This forces users to re-login every morning or after periods of inactivity. **Related Issues:** - #6559 - Claude subscription token expires after a period of time - #4992 - Getting "Unauthorized: token expired" during conversation ## Root Cause Anthropic's OAuth refresh tokens appear to have a limited lifetime. When the access token expires and OpenCode attempts to refresh it, the refresh token itself may have already been invalidated by Anthropic's servers after extended periods of inactivity. The current implementation only refreshes tokens reactively (when a request fails), rather than proactively keeping them alive. ## Proposed Solution Implement a **token keep-alive mechanism** that periodically pings all logged-in Anthropic OAuth accounts to prevent token expiration: 1. **Use the existing `/api/oauth/usage` endpoint** - This endpoint: - Already exists in the codebase (`Auth.OAuthPool.fetchAnthropicUsage`) - Requires authentication (keeps the token active) - Does NOT consume any tokens or context - Returns usage statistics (bonus: could be useful for UI) 2. **Ping interval**: Every 60 minutes - First ping after 5 minutes (to let everything settle after startup) - Then every hour while OpenCode is running 3. **Multi-account support**: Ping ALL logged-in Anthropic OAuth accounts - Users with multiple accounts (Account 1, 2, 3, etc.) will have all accounts kept alive - Only affects Anthropic provider (other providers like Copilot, OpenAI are not affected) ## Implementation New module: `packages/opencode/src/auth/keepalive.ts` ```typescript // Pings all Anthropic OAuth accounts every hour // Uses /api/oauth/usage endpoint (no token cost) // Logs success/failure for debugging ``` Integrated into `InstanceBootstrap()` so it starts automatically when OpenCode runs. ## Benefits - **No more morning re-logins** - Tokens stay fresh as long as OpenCode is running - **Zero cost** - Uses usage endpoint, no tokens consumed - **Multi-account friendly** - All accounts stay active - **Non-intrusive** - Runs in background, logged for debugging - **Automatic** - No user action required ## Testing - Run OpenCode for extended periods - Monitor logs for `auth.keepalive` messages - Verify tokens remain valid after previously problematic timeframes
yindo closed this issue 2026-02-16 18:04:46 -05:00
Author
Owner

@github-actions[bot] commented on GitHub (Jan 17, 2026):

This issue might be a duplicate of existing issues. Please check:

  • #6559: Claude subscription token expires after a period of time - same issue with 'Token refresh failed: 400' after inactivity
  • #4992: Getting "Unauthorized: token expired" during conversation - similar token expiration during usage
  • #6083: Token refresh failed: 403 failed with github copilot - same token refresh failure pattern (different HTTP status code 403 vs 400)

Feel free to ignore if none of these address your specific case.

@github-actions[bot] commented on GitHub (Jan 17, 2026): This issue might be a duplicate of existing issues. Please check: - #6559: Claude subscription token expires after a period of time - same issue with 'Token refresh failed: 400' after inactivity - #4992: Getting "Unauthorized: token expired" during conversation - similar token expiration during usage - #6083: Token refresh failed: 403 failed with github copilot - same token refresh failure pattern (different HTTP status code 403 vs 400) Feel free to ignore if none of these address your specific case.
Author
Owner

@mguttmann commented on GitHub (Jan 17, 2026):

Thanks for flagging the related issues! This issue is actually intended to fix the problems described in #6559 and #4992.

Those issues describe the problem (token expiration after inactivity), while this issue proposes a concrete solution: a keep-alive mechanism that prevents token expiration by periodically pinging the Anthropic API.

The implementation is ready in PR #9112.

@mguttmann commented on GitHub (Jan 17, 2026): Thanks for flagging the related issues! This issue is actually intended to **fix** the problems described in #6559 and #4992. Those issues describe the problem (token expiration after inactivity), while this issue proposes a concrete solution: a keep-alive mechanism that prevents token expiration by periodically pinging the Anthropic API. The implementation is ready in PR #9112.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: anomalyco/opencode#6616