BerriAI/litellm · critical · GetAPIKeyError

Failed to refresh API key: {e}

Error message

Failed to refresh API key: {e}

What it means

Raised in get_api_key() when the underlying token refresh (_refresh_api_key) exhausts its own retry loop and throws RefreshAPIKeyError, which is then wrapped as GetAPIKeyError(401). The inner message (from the original failure — HTTP errors or unexpected exceptions during refresh) is chained into this message. Practically this means the cached Copilot access token can no longer be exchanged for a Copilot session API key.

Source

Thrown at litellm/llms/github_copilot/authenticator.py:126

            api_key_info = self._refresh_api_key()
            with open(self.api_key_file, "w") as f:
                json.dump(api_key_info, f)
            token: Final = api_key_info.get("token")
            if token:
                return token
            else:
                raise GetAPIKeyError(
                    message="API key response missing token",
                    status_code=401,
                )
        except OSError as e:
            verbose_logger.error("Error saving API key to file: %s", e)
            raise GetAPIKeyError(
                message=f"Failed to save API key: {e}",
                status_code=500,
            )
        except RefreshAPIKeyError as e:
            raise GetAPIKeyError(
                message=f"Failed to refresh API key: {e}",
                status_code=401,
            )

    def get_api_base(self) -> str | None:
        """
        Get the API endpoint from the api-key.json file.

        Returns:
            Optional[str]: The GitHub Copilot API endpoint, or None if not found.
        """
        try:
            with open(self.api_key_file, "r") as f:
                api_key_info: Final = json.load(f)
                endpoints: Final = api_key_info.get("endpoints", {})
                api_endpoint: Final = endpoints.get("api")
                return api_endpoint
        except (OSError, json.JSONDecodeError, KeyError) as e:

View on GitHub (pinned to 6c2dcb801b)

Solutions

  1. Re-run the device-flow login to obtain a fresh access token (delete cached token files, then authenticate interactively once).
  2. Confirm your GitHub account still has an active Copilot subscription/seat and the OAuth grant was not revoked (GitHub Settings -> Applications).
  3. Check verbose logs for the per-attempt refresh errors — 401 means dead access token, 403/429 means rate limits or entitlement loss.
  4. Upgrade litellm: the Copilot authenticator's endpoints and required headers (editor-version etc.) have changed across releases.

Example fix

# before: expired cached access token -> RefreshAPIKeyError -> GetAPIKeyError(401) on every call
import litellm
litellm.completion(model="github_copilot/claude-sonnet-4", messages=[...])

# after: clear cache and re-auth via device flow, then resume
import shutil, pathlib
cache = pathlib.Path("~/.litellm/github_copilot").expanduser()
if cache.exists(): shutil.rmtree(cache)
# run `litellm --login github_copilot` once, verify at github.com/login/device
litellm.completion(model="github_copilot/claude-sonnet-4", messages=[...])
Defensive patterns

Strategy: fallback

Validate before calling

import json, pathlib, time

info_path = pathlib.Path("~/.litellm/github_copilot/api-key.json").expanduser()
if info_path.exists():
    info = json.loads(info_path.read_text())
    expires = info.get("expires_at", 0)
    if expires and time.time() > expires - 300:
        print("warning: Copilot api-key near/past expiry — expect refresh; ensure access token is still valid")

Try / catch

from litellm.exceptions import AuthenticationError

try:
    resp = litellm.completion(model="github_copilot/gpt-4o", messages=msgs)
except AuthenticationError as e:
    if "Failed to refresh API key" in str(e):
        # access token dead past refresh window — only cure is re-login
        raise SystemExit("Copilot session expired; re-run 'litellm --login github_copilot' to re-authenticate") from e
    raise

Prevention

When it happens

Trigger: The Copilot token endpoint rejects the stored access token on every retry attempt: access token expired past its refresh window (cached creds too old), GitHub revoked the OAuth grant, or the refresh HTTP call fails 3 times (5xx/rate-limit/network). Each underlying error is logged via verbose_logger.error before the wrapper raises.

Common situations: Long-lived deployments whose cached access token expired and cannot refresh (GitHub access tokens for Copilot internal flow are short-lived); users revoking the GitHub Copilot app authorization; sustained GitHub API rate limiting from too many refresh calls; litellm version drift where the refresh endpoint/headers changed (editor version headers matter).

Related errors


AI-assisted analysis of BerriAI/litellm@6c2dcb801b (2026-08-15). Data as JSON: /api/errors/0e2c9cce71770618. Report an issue: GitHub.