One evening I was translating store listings for one of my apps into a dozen languages, and Claude simply stopped. The cursor kept blinking. No error, no message. I decided it must be busy, waited about fifteen minutes, and then found the real cause: an old tab I'd left open had lost its session hours earlier.
What stung wasn't the wait. It was that I never went to look. Since then, whenever Claude stalls, I decide where it stalled before I touch anything, and I'd like to share that order with you here, in the sequence that has actually held up in my own day-to-day use.
If you use Claude in the browser, in Claude Code, or through the API in unattended jobs, the same sequence applies.
Three places to look first when nothing comes back
The most tempting move when Claude goes quiet is to resend the same prompt again and again. That adds no information, and it can pull you into the rate limits I'll describe later.
There are only three places worth checking: the service, your browser, and your account.
The service: check the status page once
Open the Anthropic status page and look for an incident or maintenance notice. If something is yellow or red there, almost nothing on your side will help.
My rule is to try one other browser or device a single time. If the symptom repeats, I stop and wait. Whether I can move to another task while it recovers changes how tired I feel at the end of the day.
The browser: suspect old tabs and expired sessions
This was my mistake from the opening story. A tab that has been open for hours can look perfectly alive while its session is long gone.
| Check | What to do | What it tells you |
|---|---|---|
| Close and reopen | Open claude.ai in a fresh tab | If the session had expired, this alone brings it back |
| Private window | Open it with no extensions and no cache | If it works there, an extension or cache is the cause |
| Developer console | F12 → Console, look for red lines | Distinguishes a network drop from a script failure |
| Another browser | Chrome user? Try Firefox or Safari | Separates browser-specific trouble from everything else |
Clearing cache and cookies does help sometimes, but I leave it for last. It logs you out of everything else while telling you very little about the cause.
The account: sign-in state and plan status
Sign out and back in, then look at your account settings for any warning about the plan or payment. Those two checks identify nearly every account-related stall.
Only when it's none of these three do I move on to the "slow" checklist below.
When it's slow, check these in order
"Not responding" and "slow" are different: slow means it does come back if you wait. In that case the cause is usually one of four things—the weight of the input, the model and extended-thinking settings, your connection, or load on the service.
Input weight
Several images, multiple long PDFs, a few thousand lines of code pasted whole: any of these pushes back the moment the first character appears.
What the translation work taught me is that sending less per request shortens the total time, not just each turn. Three languages per prompt, rather than a dozen, finished sooner even counting the extra rounds.
Model and extended thinking
A heavy model with extended thinking switched on spends its thinking time as your waiting time. For a quick draft or a summary, switching to a lighter model and turning thinking off changes the feel immediately.
Older doesn't always mean slower, either. Once I started splitting work into "heavy tasks on the heavy model, prep work on the light one," the moments I sat waiting became rare.
Connection
If other sites load normally and video plays without stalling, your line is fine. If you're on a VPN, disconnecting once is a cheap test.
Busy hours: measure your own
General advice about "peak hours" never matched what I saw. Time zones, models, and habits differ from person to person.
So I started logging the time to first token a few times a day through the API. After a few days, my own busy windows were plain to see.
import csv
import time
from datetime import datetime
from anthropic import Anthropic
# Measurement only: disable automatic retries so we see the raw response time
client = Anthropic(api_key="YOUR_API_KEY", timeout=60.0, max_retries=0)
def probe(model: str = "claude-sonnet-4-6") -> tuple[float, float]:
"""Return seconds to first token and seconds to completion."""
t0 = time.monotonic()
first = None
with client.messages.stream(
model=model,
max_tokens=64,
messages=[{"role": "user", "content": "Reply with OK only."}],
) as stream:
for _ in stream.text_stream:
if first is None:
first = time.monotonic() - t0
return (first or 0.0), time.monotonic() - t0
if __name__ == "__main__":
ttft, total = probe()
with open("latency_log.csv", "a", newline="") as f:
csv.writer(f).writerow([datetime.now().isoformat(timespec="minutes"), f"{ttft:.2f}", f"{total:.2f}"])
print(f"first token: {ttft:.2f}s / total: {total:.2f}s")max_retries=0 matters here: automatic retries would smooth out the very spikes you're trying to see. Streaming and timing the first token, rather than the whole answer, reflects load more honestly than total time does.
Run it morning, midday, and evening for a week and the slow band that overlaps your own schedule will stand out. I move the work I don't mind waiting on into that band.
When the answer stops partway through
A mid-answer stop comes from one of three things: the output limit, a dropped connection, or a struggling browser. The quickest test is to type "please continue" and see whether more text arrives.
If it continues, you hit the output limit. For long deliverables, asking for the outline first and then one section at a time produces less rework in the end.
If nothing continues and there's a failed request in the Network tab of the developer tools, the connection dropped. Restore it and resend once.
If the whole browser feels heavy or tabs are crashing, it's the browser. Close tabs, update it, and if that fails, fall back to the desktop app or another browser.
When Claude Code stops moving
Unlike the browser, Claude Code can stall for reasons that live inside the agent's own behavior. These are the three I've personally hit.
Auto Mode waits on approval
Confirmation dialogs keep appearing, or the screen sits on "attempting to stop" without moving. Usually a command has been flagged as needing review and the agent is waiting for you.
First, check the log to see which tool call it's waiting on. If it's something safe to allow, such as running tests or a build, add it to the allow list with /permissions and that class of prompt stops interrupting you.
If you can't escape the approval loop, press Esc, then restate the task with a narrower scope: "you may run only X inside this directory." Granting a little at a time turns out to be faster than pushing through with broad permissions.
The edit tool fails and halts
Edits fail with "string not found" or "multiple matches," and the run stops there. Almost always, the file wasn't re-read before editing, or the target string wasn't unique.
Have it re-read the file first, and specify the replacement with enough surrounding lines to be unique. A single differing space in indentation, or a full-width character where the file has a half-width one, is enough to fail, so I've learned to watch for that when pasting.
If it stops on a write-permission error, check whether the file is read-only or held by another process.
MCP servers won't connect
The server doesn't start, the tools don't appear, or the connection times out—these three symptoms almost always come down to configuration or startup order.
Run claude mcp list and check each server's state. If one shows failed, compare the command path and arguments in the config against your actual environment; a relative path or an uninstalled binary fails on the spot.
If the server starts but the tools are unusable, restart Claude Code so it reloads. Servers behind OAuth can drop suddenly when a token expires, so re-authenticating belongs in the checklist too.
Browser and app quirks
Chrome: suspect extensions
If it works in a private window but not a normal one, an extension is interfering. Disable ad blockers and translation extensions all at once, then re-enable one at a time to find the culprit.
Safari: site data and content blockers
In Safari, removing only claude.ai's site data from the Privacy settings and reopening the page often brings it back. If you run a content blocker, exempt claude.ai before anything else.
iOS / Android apps
Update from the store; if that doesn't help, delete and reinstall. When I'm in a hurry, I switch to the browser version and fix the app later so my hands don't stop.
Spotting a "stall" in unattended jobs
While you're chatting, slow just means waiting. Once Claude sits inside a scheduled job or an API batch, everything changes: one call that never returns can freeze the entire pipeline at an hour when nobody is watching.
I run site maintenance and parts of my apps' backends on schedules as an indie developer. One morning I found a whole run had died because a single API call hung. No error—just "started" in the log and nothing after. That silence still feels like the most dangerous failure to me.
Separate three states
- Slow: it takes a while, but it does return. Either the input is long or the service is busy
- Hung: it never returns. You're holding a connection that has already died
- Rate limited: you get a 429, an explicit refusal. Waiting fixes it
In conversation you can feel the difference between slow and hung. In an unattended job, the code has to tell them apart for you.
Don't wait forever: set a ceiling, log when you cross it, and retry a fixed number of times. Since I made my jobs follow that one sentence, none of them has died quietly.
Use the SDK's timeout and automatic retries fully
The Python SDK already supports this discipline. timeout caps how long a single request may wait; max_retries sets how many times the SDK retries connection errors, 408, 409, 429, and 5xx responses on its own (the default is 2, with exponential backoff and respect for the retry-after header).
import time
import anthropic
from anthropic import Anthropic
# Wait at most 60s per request; let the SDK retry transient failures up to 3 times
client = Anthropic(api_key="YOUR_API_KEY", timeout=60.0, max_retries=3)
def ask(prompt: str, model: str = "claude-sonnet-4-6") -> str:
"""Surface a hang as an exception instead of swallowing it."""
try:
resp = client.messages.create(
model=model,
max_tokens=1024,
messages=[{"role": "user", "content": prompt}],
)
return resp.content[0].text
except anthropic.RateLimitError as e:
# Still 429 after the automatic retries: record the wait and pass it up
retry_after = e.response.headers.get("retry-after", "unknown")
raise RuntimeError(f"rate limited (retry-after={retry_after}s)") from e
except anthropic.APITimeoutError as e:
# 60s per attempt, retries exhausted: record that it stopped here
raise RuntimeError("request timed out after retries") from e
except anthropic.APIConnectionError as e:
raise RuntimeError("could not reach the API") from e
if __name__ == "__main__":
t0 = time.monotonic()
try:
print(ask("Summarize this sentence in a few words."))
finally:
print(f"elapsed: {time.monotonic() - t0:.1f}s")My older version wrapped calls in a hand-written for loop and forgot to pass timeout inside it. The SDK's retries stacked on top of mine, and each failure cost three times the wait I'd planned for. Now I've thrown the loop away, let max_retries do its job, and log only what kind of failure occurred.
One more trap: if you request a very long output without streaming, the SDK may refuse the request and ask you to stream instead. For large max_tokens, reach for client.messages.stream from the start.
If it stopped, say it stopped
Raising at the end is deliberate. In an unattended job, quietly swallowing a failure is the hardest bug to find. If it stopped, leave that in the log—reading it the next morning is enough to decide what to fix.
One thing to do today
Before you resend a prompt that went quiet, glance at the status page once. I may be missing something, but that single habit has saved me more time than any other fix here.
If you run unattended jobs, add the one line Anthropic(timeout=60.0, max_retries=3) and read tomorrow morning's log. That's where I started.