BerriAI/litellm · error · GetAPIKeyError
API key response missing token
Error message
API key response missing token
What it means
Raised in get_api_key() when the Copilot API-key refresh HTTP call succeeded (status 2xx) and the response was saved, but the parsed JSON contains no 'token' field (token is None/missing). It is a 401 GetAPIKeyError, signalling that GitHub returned an unexpected 200-body from the api.github.com/copilot_internal/v2/token endpoint — typically an auth-passthrough or contract change rather than a normal expiry (expiry paths raise RefreshAPIKeyError instead).
Source
Thrown at litellm/llms/github_copilot/authenticator.py:115
message="API key expired",
status_code=401,
)
except OSError:
verbose_logger.warning("No API key file found or error opening file")
except (json.JSONDecodeError, KeyError) as e:
verbose_logger.warning("Error reading API key from file: %s", e)
except APIKeyExpiredError:
pass # Already logged in the try block
try:
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.View on GitHub (pinned to 6c2dcb801b)
Solutions
- Delete the cached token files (api-key.json and access-token file under the token dir, e.g. ~/.copilot-like dir) to force a clean re-auth, then retry.
- Test the refresh endpoint directly with your cached access token and inspect the JSON fields it actually returns.
- Disable/allowlist proxies for api.github.com — response interception commonly rewrites the body.
- If GitHub changed the response shape, update litellm to the latest version where the Copilot authenticator tracks the current endpoint contract.
Example fix
# before: corrupted/legacy api-key.json without "token" -> GetAPIKeyError 401 every call
import litellm
litellm.completion(model="github_copilot/gpt-4o", messages=[...])
# after: clear the stale cache to force a fresh OAuth + refresh cycle
import shutil, pathlib
cache = pathlib.Path.home() / ".litellm" / "github_copilot" # token dir used by the authenticator
if cache.exists():
shutil.rmtree(cache)
litellm.completion(model="github_copilot/gpt-4o", messages=[...]) # re-authenticates cleanly Defensive patterns
Strategy: fallback
Validate before calling
import json, pathlib
cache = pathlib.Path("~/.litellm/github_copilot/api-key.json").expanduser()
if cache.exists():
try:
data = json.loads(cache.read_text())
except json.JSONDecodeError:
data = {}
if not data.get("token"):
cache.unlink() # force a clean refresh instead of failing on the bad cache Type guard
def has_valid_cached_api_key(cache_path: pathlib.Path) -> bool:
"""True if the Copilot api-key cache holds a non-empty token."""
if not cache_path.exists():
return False
try:
return bool(json.loads(cache_path.read_text()).get("token"))
except (json.JSONDecodeError, OSError):
return False Try / catch
from litellm.exceptions import AuthenticationError
try:
resp = litellm.completion(model="github_copilot/gpt-4o", messages=msgs)
except AuthenticationError as e:
if "missing token" in str(e):
clear_copilot_cache() # delete api-key.json + access token files
resp = litellm.completion(model="github_copilot/gpt-4o", messages=msgs) # one clean retry
else:
raise Prevention
- Treat the api-key.json cache as opaque — never hand-edit it.
- Validate pre-seeded caches contain a non-empty 'token' in CI.
- Bypass proxies for api.github.com so refresh responses are not rewritten.
- Clear the cache and retry once when this specific error appears; escalate if it recurs.
When it happens
Trigger: The token refresh returns 200 with a JSON body lacking 'token' — e.g. an intercepted/rewritten response from a proxy, an unexpected GitHub response shape, or an api-key.json cache file that was hand-edited/corrupted so api_key_info parsed from cache has no token.
Common situations: Corporate proxies stripping Authorization headers so the endpoint returns an anonymous 200 JSON error body; GitHub changing the internal Copilot token endpoint response (these internal endpoints are undocumented and can shift); truncated or corrupted api-key.json from concurrent writers.
Related errors
- Failed to refresh API key: {e}
- Failed to refresh API key after maximum retries
- Failed to get access token after 3 attempts
- Failed to save API key: {e}
- str(e)
AI-assisted analysis of BerriAI/litellm@6c2dcb801b (2026-08-15).
Data as JSON: /api/errors/1f37f4343fc5e293.
Report an issue: GitHub.