BerriAI/litellm · error · GetAPIKeyError

Failed to save API key: {e}

Error message

Failed to save API key: {e}

What it means

Raised in get_api_key() when writing the refreshed api-key JSON to disk raises OSError — the token cache file (api_key_file) cannot be created or written. The refresh itself succeeded (the API key was obtained), but persisting it failed, so litellm aborts with GetAPIKeyError 500. The underlying OSError text (permission denied, read-only file system, disk quota, directory missing) is embedded in the message.

Source

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

            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.

        Returns:
            Optional[str]: The GitHub Copilot API endpoint, or None if not found.
        """
        try:
            with open(self.api_key_file, "r") as f:

View on GitHub (pinned to 6c2dcb801b)

Solutions

  1. Check the OSError detail in the message — 'Permission denied' vs 'Read-only file system' vs 'No space left on device' points to the fix.
  2. Fix ownership/permissions of the token directory: chown -R $(id -u):$(id -g) <token_dir> && chmod -R u+rwX <token_dir>.
  3. In containers, mount a writable volume (or emptyDir) at the token dir path and run with a consistent UID.
  4. Free disk space if ENOSPC; note the key still worked for this request only if you catch the error and use a writable dir — otherwise every call fails here.

Example fix

# before: container runs as app user, token dir owned by root -> OSError every refresh
# docker run -v litellm-tokens:/root/.litellm/github_copilot ...  (root-owned)

# after: writable mount with matching ownership
# docker run --user 1000:1000 -v litellm-tokens:/home/app/.litellm/github_copilot ...
Defensive patterns

Strategy: validation

Validate before calling

import os, pathlib

token_dir = pathlib.Path("~/.litellm/github_copilot").expanduser()
try:
    token_dir.mkdir(parents=True, exist_ok=True)
    probe = token_dir / ".write_probe"
    probe.write_text("ok")
    probe.unlink()
except OSError as e:
    raise RuntimeError(f"Copilot token dir not writable ({e}); fix ownership/mount before use") from e

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 save API key" in str(e):
        raise RuntimeError(
            "Token cache unwritable — fix dir ownership/permissions or mount a writable volume, then retry"
        ) from e
    raise

Prevention

When it happens

Trigger: The token cache directory does not exist or is not writable by the process user: container images running as non-root with a read-only or unmapped volume for the token dir; files owned by root from a previous docker run; disk full (ENOSPC); SELinux/AppArmor denying writes; path too long (ENAMETOOLONG).

Common situations: Docker/Kubernetes deployments where the token dir lands on a read-only layer or a volume mounted with wrong ownership (root-owned after running once as root, then as UID 1000); ephemeral CI environments with full disks; macOS/Windows permission restrictions on the home directory.

Related errors


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