openclaw - ✅(Solved) Fix GitHub Copilot auth profile rotation ineffective — all profiles share single cached API token [1 pull requests, 1 comments, 1 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
openclaw/openclaw#43658Fetched 2026-04-08 00:16:14
View on GitHub
Comments
1
Participants
1
Timeline
8
Reactions
0
Participants
Timeline (top)
referenced ×6cross-referenced ×2

When multiple GitHub Copilot auth profiles are configured for round-robin rotation, hitting an API rate limit on one profile does not effectively rotate to the next profile. The system detects the rate limit correctly and advances to the next profile, but all subsequent requests continue to use the same Copilot API token from the rate-limited account.

Error Message

  1. The request with bob still fails with the same rate limit error.

Root Cause

// github-copilot-token.ts — current cache check (simplified)
const cached = loadJsonFile(cachePath);
if (cached && isTokenUsable(cached)) {
  return cached; // ← always returns this regardless of which GitHub PAT was passed in
}

The cache has no concept of "which profile produced this token", so a profile switch is invisible to the token exchange layer.

Fix Action

Fixed

PR fix notes

PR #43659: fix: copilot token cache ignores profile rotation on rate limit

Description (problem / solution / changelog)

Closes #43658

Problem

When multiple github-copilot auth profiles are configured, profile rotation after a rate limit has no effect. All profiles end up using the same Copilot API token because resolveCopilotApiToken() caches to a single shared file (github-copilot.token.json) without tracking which GitHub account produced the token. A profile switch passes a different GitHub PAT, but the cache returns the still-valid token from the rate-limited account.

Solution

Store a SHA-256 fingerprint (first 16 hex chars) of the GitHub PAT alongside the cached Copilot API token. On cache lookup, compare the fingerprint against the current profile's token. A mismatch forces a fresh token exchange with the GitHub Copilot token endpoint.

Changes

src/providers/github-copilot-token.ts

  • Added githubTokenFingerprint field to CachedCopilotToken type
  • Added fingerprintGithubToken() helper (SHA-256, truncated to 16 hex chars)
  • Cache hit now requires fingerprint match in addition to expiry check
  • New tokens are saved with the fingerprint of the GitHub PAT used

src/providers/github-copilot-token.test.ts

  • Updated existing cache-hit test to include fingerprint
  • Added test: cache bypassed when a different GitHub token (different profile) is used
  • Added test: legacy cache entries without fingerprint are treated as cache miss

Backward compatibility

  • Legacy cache files without githubTokenFingerprint are treated as a cache miss — the token is re-fetched on the next call and saved with a fingerprint. No migration needed.
  • The githubTokenFingerprint field is optional (?) so older versions reading a new cache file won't break.

Testing

✓ derives baseUrl from token
✓ uses cache when token is still valid
✓ fetches and stores token when cache is missing
✓ bypasses cache when a different github token (profile) is used
✓ bypasses cache when cached entry has no fingerprint (legacy)

Test Files  1 passed (1)
     Tests  5 passed (5)

All 38 auth-profiles tests also pass.

Changed files

  • src/providers/github-copilot-token.test.ts (modified, +70/-0)
  • src/providers/github-copilot-token.ts (modified, +17/-1)

Code Example

// github-copilot-token.ts — current cache check (simplified)
const cached = loadJsonFile(cachePath);
if (cached && isTokenUsable(cached)) {
  return cached; // ← always returns this regardless of which GitHub PAT was passed in
}
RAW_BUFFERClick to expand / collapse

Summary

When multiple GitHub Copilot auth profiles are configured for round-robin rotation, hitting an API rate limit on one profile does not effectively rotate to the next profile. The system detects the rate limit correctly and advances to the next profile, but all subsequent requests continue to use the same Copilot API token from the rate-limited account.

Steps to Reproduce

  1. Configure two or more github-copilot auth profiles (e.g. github-copilot:alice, github-copilot:bob), each with a distinct GitHub PAT.
  2. Use the system until profile alice hits a rate limit (⚠️ API rate limit reached).
  3. Observe that the profile rotation logic selects bob as the next candidate.
  4. The request with bob still fails with the same rate limit error.

Expected Behavior

After rotation, the Copilot API token should be exchanged using bob's GitHub PAT, producing a distinct Copilot API token tied to bob's account and quota.

Actual Behavior

resolveCopilotApiToken() in src/providers/github-copilot-token.ts caches the Copilot API token in a single shared file (credentials/github-copilot.token.json) without recording which GitHub account produced it. When the rotated profile calls this function with a different GitHub PAT, the cache check passes (token still has >5 min remaining) and returns the original rate-limited token — effectively bypassing rotation.

Root Cause

// github-copilot-token.ts — current cache check (simplified)
const cached = loadJsonFile(cachePath);
if (cached && isTokenUsable(cached)) {
  return cached; // ← always returns this regardless of which GitHub PAT was passed in
}

The cache has no concept of "which profile produced this token", so a profile switch is invisible to the token exchange layer.

Impact

  • All configured GitHub Copilot profiles are effectively treated as one account for rate-limit purposes.
  • Profile rotation and cooldown logic work correctly at the auth-profiles layer but are nullified at the token exchange layer.
  • Users who configure multiple accounts for higher aggregate throughput see no benefit.

Environment

  • OpenClaw version: any version with multi-profile Copilot support
  • Provider: github-copilot
  • Auth mode: token (multiple profiles)

extent analysis

Fix Plan

To resolve the issue, we need to modify the resolveCopilotApiToken() function to cache tokens per GitHub account. Here are the steps:

  • Modify the cache file path to include the GitHub account name.
  • Update the cache check to use the new cache file path.
  • Store the GitHub account name with the cached token.

Code Changes

// github-copilot-token.ts — updated cache check
const cachePath = `credentials/github-copilot-${githubAccountName}.token.json`;
const cached = loadJsonFile(cachePath);
if (cached && isTokenUsable(cached)) {
  return cached;
} else {
  // Generate a new token and store it in the cache
  const newToken = generateToken(githubAccountName);
  saveJsonFile(cachePath, newToken);
  return newToken;
}

Verification

To verify the fix, follow these steps:

  • Configure multiple GitHub Copilot auth profiles.
  • Hit the API rate limit on one profile.
  • Verify that the system rotates to the next profile and uses a distinct Copilot API token.
  • Check the cache files to ensure that each profile has its own token file.

Extra Tips

  • Make sure to handle cache file naming conflicts if multiple profiles have the same GitHub account name.
  • Consider implementing a cache expiration mechanism to ensure that tokens are updated periodically.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING