BerriAI/litellm · error · UpError

Login did not produce a usable token; cannot start `lite up`

Error message

Login did not produce a usable token; cannot start `lite up`.

What it means

Raised by `lite up` after it invoked the interactive login flow (ctx.invoke(login)) but the reloaded token still fails the usability check: missing, wrong base_url, or stale per is_cli_token_fresh. It means the login flow returned without raising yet did not persist a token usable for this proxy — e.g. the browser SSO was never completed, or the token that landed in ~/.litellm/token.json belongs to a different server. `up` aborts rather than patch Claude Code settings with an apiKeyHelper that would fail on every request.

Source

Thrown at litellm/proxy/client/cli/commands/up.py:175


def _ensure_fresh_login(ctx: click.Context) -> None:
    base_url: Final = ctx.obj["base_url"].rstrip("/")
    token_data = load_token()
    if token_data and token_data.get("base_url") == base_url and is_cli_token_fresh(token_data):
        return

    if not sys.stdin.isatty():
        raise UpError(
            "No fresh LiteLLM login found for this proxy. Run `lite login` first (apiKeyHelper "
            "reads this token on every Claude Code request)."
        )

    click.echo("No fresh LiteLLM login found for this proxy; starting login...")
    ctx.invoke(login)
    token_data = load_token()
    if not token_data or token_data.get("base_url") != base_url or not is_cli_token_fresh(token_data):
        raise UpError("Login did not produce a usable token; cannot start `lite up`.")


def _restore_and_report() -> None:
    record: Final = restore_claude_settings()
    if record is None:
        click.echo("Nothing to restore.")
        return
    if record.existed:
        click.echo(f"Restored {CLAUDE_SETTINGS_PATH} to its original contents.")
    else:
        click.echo(f"Removed {CLAUDE_SETTINGS_PATH} (it did not exist before `lite up`).")


@click.command(name="up")
@click.pass_context
def up(ctx: click.Context) -> None:
    """Route every Claude Code session through your LiteLLM proxy until stopped.

View on GitHub (pinned to 77b7c6c40c)

Solutions

  1. Run `lite login` standalone in the same terminal against the same proxy, complete the browser flow, and confirm `lite auth print-token --base-url <url>` prints a token
  2. Verify ~/.litellm/token.json afterwards: `base_url` must exactly match the proxy `up` uses and `timestamp` must be fresh
  3. If no browser can open on this machine, run `lite login` where one can and copy ~/.litellm/token.json (mode 0600) over
  4. Check for a concurrent LiteLLM login/logout (CI job, another terminal) rewriting the token file

Example fix

# before
lite up
# "No fresh LiteLLM login found; starting login..."
# UpError: Login did not produce a usable token; cannot start `lite up`.

# after
lite login --base-url https://proxy.internal   # complete SSO in the browser
lite auth print-token --base-url https://proxy.internal   # sanity check
lite up
Defensive patterns

Strategy: retry

Validate before calling

import subprocess

def ensure_login() -> None:
    if subprocess.call(["lite", "auth", "print-token", "--base-url", BASE_URL]) != 0:
        subprocess.check_call(["lite", "login", "--base-url", BASE_URL])  # interactive repair
        subprocess.check_call(["lite", "auth", "print-token", "--base-url", BASE_URL])  # verify

Try / catch

import subprocess, sys
for attempt in (1, 2):
    rc = subprocess.call(["lite", "up"])
    if rc == 0:
        break
    if attempt == 1:
        subprocess.call(["lite", "login"])  # one interactive repair, then one retry
    else:
        sys.exit("`lite up` failed twice — inspect ~/.litellm/token.json manually")

Prevention

When it happens

Trigger: Canceling or never opening the browser during `lite login`'s device/SSO flow; logging in against a different --base-url than the one `up` targets; the login poll finishing without writing token.json; a concurrent process (logout, second login) overwriting the token file between login and the re-check.

Common situations: Headless/SSH session with no browser so SSO never completed; clock skew on the machine making a just-issued token appear stale; proxy behind a URL rewrite so the stored base_url differs from the CLI's; two terminals running LiteLLM logins against different proxies clobbering one token.json.

Related errors


AI-assisted analysis of BerriAI/litellm@77b7c6c40c (2026-08-18). Data as JSON: /api/errors/8ebabcf9a06083d4. Report an issue: GitHub.