{"record":{"id":"0032d38528004868","repo":"BerriAI/litellm","slug":"no-fresh-litellm-login-found-for-this-proxy-run","errorCode":null,"errorMessage":"No fresh LiteLLM login found for this proxy. Run `lite login` first (apiKeyHelper reads this token on every Claude Code request).","messagePattern":"No fresh LiteLLM login found for this proxy\\. Run `lite login` first \\(apiKeyHelper reads this token on every Claude Code request\\)\\.","errorType":"exception","errorClass":"UpError","httpStatus":null,"severity":"error","filePath":"litellm/proxy/client/cli/commands/up.py","lineNumber":166,"sourceCode":"    whichever server `up` currently points at.\n    \"\"\"\n    lite_path: Final = shutil.which(\"lite\")\n    if lite_path is None:\n        raise UpError(\n            \"Could not find `lite` on your PATH. Claude Code's apiKeyHelper needs \"\n            \"an absolute path to it, so `lite up` cannot continue.\"\n        )\n    return f\"{shlex.quote(lite_path)} auth print-token --base-url {shlex.quote(base_url)}\"\n\n\ndef _ensure_fresh_login(ctx: click.Context) -> None:\n    base_url: Final = ctx.obj[\"base_url\"].rstrip(\"/\")\n    token_data = load_token()\n    if token_data and token_data.get(\"base_url\") == base_url and is_cli_token_fresh(token_data):\n        return\n\n    if not sys.stdin.isatty():\n        raise UpError(\n            \"No fresh LiteLLM login found for this proxy. Run `lite login` first (apiKeyHelper \"\n            \"reads this token on every Claude Code request).\"\n        )\n\n    click.echo(\"No fresh LiteLLM login found for this proxy; starting login...\")\n    ctx.invoke(login)\n    token_data = load_token()\n    if not token_data or token_data.get(\"base_url\") != base_url or not is_cli_token_fresh(token_data):\n        raise UpError(\"Login did not produce a usable token; cannot start `lite up`.\")\n\n\ndef _restore_and_report() -> None:\n    record: Final = restore_claude_settings()\n    if record is None:\n        click.echo(\"Nothing to restore.\")\n        return\n    if record.existed:\n        click.echo(f\"Restored {CLAUDE_SETTINGS_PATH} to its original contents.\")","sourceCodeStart":148,"sourceCodeEnd":184,"githubUrl":"https://github.com/BerriAI/litellm/blob/77b7c6c40c0c5aa5fbcb1d6a1825ac39ca8829b8/litellm/proxy/client/cli/commands/up.py#L148-L184","documentation":"Raised by `lite up`'s _ensure_fresh_login when stdin is not a TTY and the cached CLI token (~/.litellm/token.json) is missing, was issued for a different proxy base_url, or is stale per is_cli_token_fresh (age >= CLI_JWT_EXPIRATION_HOURS minus a 0.1h buffer). In an interactive terminal `up` would offer to start the login flow; with no TTY it cannot, so it fails fast and asks you to run `lite login` first. The token matters because Claude Code's apiKeyHelper re-reads it on every request via `lite auth print-token --base-url`.","triggerScenarios":"Running `lite up` in a script, CI job, nohup, or `ssh -T` (stdin not a TTY) after the cached JWT expired, after the token file was deleted (`lite logout`), after switching --base-url/LITELLM_PROXY_URL to a proxy never logged into, or when token.json's base_url doesn't match the current proxy URL.","commonSituations":"Long-lived CI runner where the token aged past the CLI JWT expiry window; developer pointing the CLI at a second proxy (staging vs prod) without re-logging in; cleanup step wiping ~/.litellm; token minted for http://host:4000 while the CLI now targets http://host:4000/ (trailing-slash mismatches are normalized, host/port ones are not).","solutions":["Run `lite login` interactively against the same proxy (`--base-url` / LITELLM_PROXY_URL must match what `up` uses), complete the browser flow, then rerun `lite up`","If it still fails, inspect ~/.litellm/token.json: the `base_url` field must equal the proxy URL (trailing slash stripped) and `timestamp` must be recent","In non-interactive pipelines, re-login before the token expires rather than after — schedule the interactive `lite login` or pre-provision token.json on the runner"],"exampleFix":"# before (CI job, no TTY)\nlite up   # UpError: No fresh LiteLLM login found for this proxy ...\n\n# after\n# one-time on a workstation with a browser:\nlite login --base-url https://proxy.internal\ncurl -s ~/.litellm/token.json -o token.json   # copy to the runner, keep mode 0600\nlite up","handlingStrategy":"validation","validationCode":"import json, os, time\n\ndef cli_token_is_fresh(base_url: str, max_age_hours: float) -> bool:\n    p = os.path.expanduser(\"~/.litellm/token.json\")\n    if not os.path.exists(p):\n        return False\n    try:\n        t = json.load(open(p))\n    except (OSError, json.JSONDecodeError):\n        return False\n    ts = t.get(\"timestamp\")\n    return (\n        t.get(\"base_url\") == base_url.rstrip(\"/\")\n        and isinstance(ts, (int, float))\n        and (time.time() - ts) / 3600 < max_age_hours\n    )\n\n# check with litellm.constants.CLI_JWT_EXPIRATION_HOURS as max_age_hours before `lite up`","typeGuard":null,"tryCatchPattern":"import subprocess, sys\nif not cli_token_is_fresh(BASE_URL, MAX_AGE_H):\n    if not sys.stdin.isatty():\n        sys.exit(\"run `lite login` interactively before this non-interactive job\")\n    subprocess.check_call([\"lite\", \"login\"])\nsubprocess.check_call([\"lite\", \"up\"])","preventionTips":["Run `lite login` from an interactive terminal right after pointing the CLI at a new --base-url","On CI runners, re-provision ~/.litellm/token.json before the JWT expiry window elapses","Keep one token.json per machine per proxy; use --base-url consistently so base_url always matches"],"tags":["litellm","cli","authentication","token-expiry","non-interactive"],"backgroundTag":"auth-token-expired","analyzedSha":"77b7c6c40c0c5aa5fbcb1d6a1825ac39ca8829b8","analyzedAt":"2026-08-18T11:44:31.656Z","schemaVersion":2},"datasetVersion":"2026-08-21T11:28:35.574Z"}