“Login expired · Please run /login” means Claude Code tried to renew your saved login automatically, was refused, and cleared the stored credentials. In this state, your prompts never reach the API; they stop on your machine. Signing in again with /login fixes it, while retrying over and over leaves the message unchanged.

Login expired · Please run /login

In non-interactive mode (claude -p) and the Agent SDK, the same state appears with the following wording (the structured error code is authentication_failed).

Failed to authenticate: OAuth session expired and could not be refreshed

Start with the exact wording of the message

More than one message asks you to run /login

Login expired · Please run /login
Renewal of the saved login was refused, and the credentials have been cleared
→ Sign in again with /login
Your prompt never reached the API
Not logged in · Please run /login
This session has no usable credentials at all
→ See how they differ
Also suspect an environment variable that wasn’t loaded
OAuth token revoked / OAuth token has expired
The API rejected a request that was sent
→ /login; if it returns, start with /logout
The difference: it was rejected after sending
Your account is on hold…
The account itself is on hold, not the login
→ Check the details via the link in the message
Signing in again won’t clear it
Based on the categories in the official error reference, this diagram helps you choose where to look from the wording of the message.

1. What exactly expired when you see Login expired?

According to Claude Code’s official error reference, the message appears after the sequence below. What expired is the claude.ai or Claude Console login (your OAuth credentials) that you saved locally with /login.

By the time renewal fails, the stored credentials are already gone

① The saved login

The claude.ai or Claude Console login you saved with /login

② Automatic renewal is attempted

Claude Code asks for renewal using the stored refresh token

③ Refused, then cleared

The OAuth service refuses, and Claude Code clears the stored credentials

④ Stopped before sending

Every later prompt stops locally with Login expired. Only /login can create new credentials

Source: a diagram of the “Login expired” entry in Claude Code’s official error reference.

Step ④ is the key. Because Claude Code has already cleared the credentials, it doesn’t send the request to the API. That’s why resending produces the same message every time; the official docs say so too: “Retrying without signing in shows the same message on every request.” The official explanation stops at the fact that renewal was refused and doesn’t break down why. It does state, however, that if the cause was the account itself being suspended rather than a stale login, you get a different message: “Your account is on hold.”

This message has been shown since v2.1.206

According to the official error reference, Claude Code before v2.1.206 kept sending requests with whatever credential remained in the environment even after the login could no longer be renewed. As a result, every model failed with “There’s an issue with the selected model” or a 401 error, and no prompt to sign in appeared. The v2.1.206 entry in the official CHANGELOG also reads “Fixed an expired login failing every model with a misleading ‘There’s an issue with the selected model’ error instead of prompting to run /login” (published to npm on July 9, 2026, UTC). If you’re on an older version and every model returns a model error, try /login first.

Authentication methods that never show this message

Sessions authenticated with an API key (ANTHROPIC_API_KEY), a long-lived token created with claude setup-token (CLAUDE_CODE_OAUTH_TOKEN), or a cloud provider such as Amazon Bedrock don’t use the saved login, so they never show this message. Put the other way around, a session that showed Login expired was using the saved login as its active credential.

A warning appears 3 days in advance

According to the official authentication docs, from v2.1.203 onward, the following warning appears at startup once your saved login is less than 3 days from expiring (5 days before v2.1.217). The warning is informational; authentication keeps working until the expiry arrives.

Your login expires in 3 days · run /login to renew

Renewing early matters most for sessions running unattended. If a background session in agent view or a Remote Control session crosses the login’s expiry, it stops making progress the moment the credentials lapse and won’t recover until you sign in again.

2. How it differs from Not logged in and OAuth token expired

All of these end by asking you to run /login, but they differ in what happened and whether the request reached the API. Here are the official error reference’s explanations side by side.

MessageWhat happenedSent to the API?Fix
Login expired · Please run /loginRenewal of the saved login was refused, and the credentials were clearedNot sent (stopped locally)/login
Not logged in · Please run /loginThis session has no usable credentials―/login, or check that the environment variable you intended is being loaded
OAuth token revoked · Please run /login / …401 OAuth token has expired…The API rejected a request that was sent (signing out everywhere, removal by an admin, or automatic renewal failing mid-session)Sent, then 401/login. If it returns in the same session, /logout → /login
Please run /login · API Error: 401 Invalid authentication credentialsThe credential’s format was accepted, but the account or organization side rejected itSent, then 401Check the active credential with /status before acting
Your account is on hold…The account is on hold (shown as Login expired before v2.1.235)―Use the link in the message to see details or appeal
Remote Control disconnected — Claude.ai login expired — …Only the Remote Control connection stopped. The local session keeps running―/login. Depending on the message, follow with /remote-control

Remember it this way: Not logged in means “there were never any credentials,” while Login expired means “there were, but they couldn’t be renewed and were cleared.” With Not logged in, the ANTHROPIC_API_KEY you meant to use may simply not be loaded in the shell you launched from. For the causes of Not logged in and how an API key environment variable takes precedence over a subscription login, see our article on Claude Code authentication and login errors (Not logged in / Invalid API key).

The difference from OAuth token has expired is whether the request was sent to the API first. The official docs explain: “When the saved login has already been cleared after a failed refresh, you see Login expired instead.” The similarly named Anthropic profile login expired appears only when you use an Anthropic credential profile selected with ANTHROPIC_PROFILE or similar, and never in sessions that use a claude.ai login.

3. Signing in again with /login

The request that showed this message never reached the API. But if it appeared mid-turn, file edits or commands from earlier in that turn may already have run, and sending the same prompt as-is could run them twice. Before resending, check how far the work had gotten.

01

Check which account’s login expired

Open /status from the input box. According to the official docs, from v2.1.210 onward the Login row reads Expired — log in again and also shows the organization and email saved for the expired login. The row appears only when the saved login is your active credential. If you switch between multiple accounts, note here which one to sign back in to.

02

Run /login and sign in with the same account

A browser opens. Sign in with your claude.ai account if you’re on a subscription, or your Claude Console account if you pay for API usage. If the browser doesn’t open, press c to copy the URL and paste it into a browser on your machine. In WSL2, over SSH, or in a container, the browser may show a login code after you sign in. In that case, paste it at the terminal’s Paste code here if prompted prompt. When you see Login successful, press Enter to return.

03

If pasting the code doesn’t work, sign in from the shell

In terminals where pasting doesn’t reach the input box, try another paste method, such as right-click or Shift+Insert in Windows Terminal. If that still fails, run claude auth login in your shell. This command reads the pasted code from standard input, and the official docs say it works on native Windows too.

04

Confirm with a short prompt before going back to work

Don’t send your original large prompt right away. Send one short check that involves no file operations. Once a response comes back, check the progress and then resend the prompt you saved.

# In Claude Code's input box
/status
/login

# When pasting into the input box doesn't work (run in your shell)
claude auth login

When it appears with claude -p or in CI

Non-interactive mode can’t complete a browser sign-in. The official procedure is to start claude once in interactive mode in the same environment, complete /login, and then rerun your original command. For automation that can’t sign in interactively, use a Console API key (ANTHROPIC_API_KEY, billed as API usage) or a long-lived token created with claude setup-token.

claude setup-token
# After you approve in the browser, the token is printed in the terminal (it isn't saved anywhere)
export CLAUDE_CODE_OAUTH_TOKEN=the-token-shown

According to the official authentication docs, this token is valid for one year and requires a Pro, Max, Team, or Enterprise plan. It can only make model requests; it can’t be used for Remote Control or claude.ai connectors. Runs with --bare don’t read this variable, so use an API key or apiKeyHelper there. Note that when this token itself expires or becomes invalid, you see the OAuth token revoked / expired message, not Login expired.

When signing in itself fails

For cases where the cause is unclear, the official troubleshooting guide gives a reset procedure: /logout → close Claude Code → start it again with claude and sign in. /logout also resets your first-run setup state, so the next launch walks you through login and initial setup again. All stored credentials are removed, so you’ll also need to re-enter MCP server logins and plugin secret values. If you see OAuth error: Invalid code, the login code either expired or was cut off when copied, so complete the steps promptly once the browser opens, and copy the whole URL with c.

4. Confirming that it’s fixed

Don’t call it done just because the error didn’t appear once. Check these three things.

What /status shows

Whether Expired — log in again is gone from the Login row, and the organization and email are the account you intended. When both a login and an API key are present, the one not in use is marked

The exit code of claude auth status

Per the official CLI reference, it exits with 0 when logged in and 1 when not. Useful as a pre-check in scripts

Whether a short response comes back

You only know a model request actually goes through once a response returns. The check message consumes usage as normal

# Human-readable output
claude auth status --text

# Check the exit code (macOS / Linux)
claude auth status; echo $?

# Check the exit code (PowerShell)
claude auth status; $LASTEXITCODE

To check for a response, send something like this.

This is a connection check. Don't read or write files or call any tools;
just reply with "Response OK."

5. What to check when it keeps coming back

If it happens once, /login is the end of it. If it appears every few days, or almost daily, work through the items below from the top. Every item comes from the official docs or the CHANGELOG; none were added on speculation. First, check whether your version is out of date.

claude --version
claude update

Parallel sessions and waking from sleep

Per the official docs, parallel sessions on the same machine share the saved login, and renewal happens one process at a time. Before v2.1.211, two sessions could renew with the same token on wake from sleep, invalidating the saved login so that every session asked you to log in at once

An older build running on the same machine

The CHANGELOG’s v2.1.277 entry says it fixed being unexpectedly logged out when an older Claude Code build, such as an IDE extension’s bundled CLI, runs on the same machine. It doesn’t say what message appeared at the time. Update both the CLI and the IDE extension

The macOS Keychain

If the Keychain is locked or its password is out of sync with your account, writes are refused and the login is saved in plain text to ~/.claude/.credentials.json. Use the steps below to make it writable again. Added September 26, 2026: the CHANGELOG’s v2.1.281 entry says it fixed writes that could delete the Keychain entry when the Keychain was locked, for example right after waking from sleep

The system clock

The official troubleshooting guide notes that token validation depends on correct timestamps and advises checking that your clock is accurate if you’re asked to log in frequently. Turn on your OS’s automatic time sync

Multiple accounts and CLAUDE_CONFIG_DIR

When CLAUDE_CONFIG_DIR is set, credentials are stored in that folder, and macOS Keychain entries are also separated per folder. If you run /login with a different value than the session that showed the message, the login goes to a different store

The same message persists after signing in again

Before v2.1.235, an account on hold was also shown as Login expired. If /login on an older version doesn’t clear it, update and then look at the message again

Making the macOS Keychain writable again

These are the steps from the official troubleshooting guide. If the claude doctor report has no warning starting with macOS Keychain is not writable, the Keychain is writable, so do step 4 only.

  1. Run claude doctor to check Keychain access
  2. If there’s a warning, unlock it with the command below, then run claude doctor again
  3. If the warning remains after unlocking, open Keychain Access, select the login keychain, and use Edit > Change Password for Keychain "login" from the official steps to resync it with your account password
  4. Once the warning is gone, sign in again with /logout → /login. The login is saved back into the Keychain
claude doctor
security unlock-keychain ~/Library/Keychains/login.keychain-db

Will switching to an API key stop this message?

Yes, but the billing model changes. Under the official precedence rules, an approved ANTHROPIC_API_KEY takes precedence over your /login login, and in -p non-interactive mode it’s always used whenever it’s set. Usage during that time is billed as Console API usage, not to your subscription. We don’t recommend setting an API key solely to avoid Login expired. If your goal is unattended runs, claude setup-token, which works with your subscription, is a better fit.

6. Reported cases with no confirmed cause

Claude Code’s GitHub repository has bug reports containing this exact message. On September 22, 2026, we opened and read the six below. All of them are user reports or guesses, and in what we read, Anthropic had not posted a public response. The same symptom doesn’t necessarily mean the same cause, so don’t copy them as procedures; use them to judge whether your situation is similar.

  • #94464 (macOS, CLI 2.1.270): In an environment where the Claude Code built into the Claude desktop app and the terminal CLI share the same ~/.claude, the terminal shows Login expired almost every day. The reporter suspects that tokens renewed by the built-in version are written only to the file, leaving an old token in the Keychain. A follow-up reports the same symptom on 2.1.273
  • #88583 (macOS, 2.1.229 bundled with the desktop app): A report that parallel sessions compete over renewal and the Keychain credentials get emptied. A follow-up includes an example of Failed to authenticate: OAuth session expired and could not be refreshed on Windows 2.1.251
  • #88947 (Linux, 2.1.228–2.1.240): In an environment where 10 to 24 interactive sessions share one ~/.claude, every session hits Login expired at intervals of under a week
  • #95822 (macOS): Points out that short-lived commands such as claude auth status start a renewal at launch and exit without saving the result, leaving an already-used refresh token behind. A monitoring tool had been running it every few minutes (observed on 2.1.259). It’s safer to avoid running the claude auth status from Section 4 repeatedly at short intervals
  • #83834 (Windows, 2.1.221): Login expired appears even right after signing in, and the expiry value in the stored file was 0. A follow-up says the same happens on Linux 2.1.241
  • #80605 (macOS, 2.1.218): The message persisted even after deleting the credentials and Keychain entry and reinstalling, and /login didn’t bring up a sign-in screen. The reporter could sign in the next day but writes that they don’t know what fixed it (closed)

What the first four have in common is multiple processes on one machine using the same saved login. If it keeps recurring, note how many instances of Claude Code are running at the same time (terminal CLI, IDE extension, desktop app, background sessions); that lets you describe the situation accurately when you report it.

7. What to record if it still isn’t fixed

The official troubleshooting guide lists these four channels for when the problem isn’t resolved.

  • Run claude doctor to generate an automatic diagnostic report
  • If you can open a session, report it with /feedback in the input box
  • Search GitHub Issues for known problems; if there’s none, file a new report with your OS, installation method, and the full error text
  • For account-side problems such as a login loop, sign in to claude.ai (Console users: platform.claude.com), click your initials in the lower left, and choose “Get help” to contact support

Report notes template

Environment
Output of claude --version / OS / installation method (native, npm, built into the desktop app, etc.)
Message
Full error text / time it occurred and time zone / the Login row from /status (mask the email)
Situation
Number and kinds of Claude Code instances running at the same time / whether it was right after waking from sleep or an update
What you tried
What changed before and after /login, /logout, Keychain checks, and so on

8. Summary

“Login expired · Please run /login” is the message you get after renewal of the saved login was refused and Claude Code cleared the credentials. Requests stop on your machine, so retrying won’t fix it; if you want to keep using the saved login, signing in again with /login is the only way. Check the expired account with /status, sign in with the same account, confirm with a short response, and you’re done.

If it keeps coming back, check in this order: updating your version, older builds or IDE extensions running on the same machine, the macOS Keychain, the clock, and CLAUDE_CONFIG_DIR. Not logged in (no credentials at all) and OAuth token has expired (rejected by the API) need different fixes, so compare the exact wording of the message first. For other errors, see our roundup of common Claude Code errors and fixes.

FAQ

Q. What does “Login expired · Please run /login” mean?
A. Claude Code tried to renew your saved login automatically, was refused, and cleared the stored credentials. From then on, requests aren’t sent to the API; they stop on your machine. Signing in again with /login fixes it.

Q. Will retrying a few times fix it?
A. No. The credentials have already been cleared, and the official docs state that “Retrying without signing in shows the same message on every request.” Only /login can create new credentials.

Q. Does this mean my subscription ended?
A. This message alone doesn’t tell you. The official explanation is that renewal of the saved login was refused. If the account itself is on hold, you get a different message: “Your account is on hold.” Check your plan status with /status or in claude.ai’s settings after signing in again.

Q. How is it different from Not logged in?
A. Not logged in means this session never had usable credentials. Login expired means there was a saved login, but it couldn’t be renewed and was cleared. Both are fixed with /login, but with Not logged in you should also check whether the environment variable you meant to use is being loaded.

Primary sources