cloudflare / cloudflare/cloudflare-os
A chat session stops working with `503 error code: 1019` part-way through; only a page reload fixes it
- Dominant language
- TypeScript
- Stars
- 9.9k
- Forks
- 1.2k
- Avg merge
- 1d 20h
- Merged PRs (30d)
- 107
Description
## Summary
On a Cloudflare-hosted deployment, a chat session stops being able to do anything after some number of MCP tool calls: every turn fails with `503 error code: 1019`, the Workers invocation limit. Retrying fails identically, forever.
**Reloading the page fixes it immediately** — same conversation, no cache clear, no new chat, same model and same MCP server. Since everything except the WebSocket survives the reload, whatever is exhausted belongs to the connection rather than to the chat, the Durable Objects, the model, or the MCP server.
One hypothesis as to the cause is that subrequests issued from inside the one long-lived `/api` WebSocket invocation draw down that invocation's budget across the whole session rather than resetting per turn, and a new socket is the only thing that restores it. I have not been able to confirm the mechanism, and the obvious version of it — a fixed count of model calls — is not something I could prove definitively.
## Environment
| | |
| --- | --- |
| Deployment | Self-hosted via [cloudflare-os-starter](https://github.com/cloudflare/cloudflare-os-starter), Cloudflare Access mode, router as public origin |
| Model | `@cf/zai-org/glm-5.2` (Workers AI) through AI Gateway |
| Connector | One MCP connector — a Gmail MCP server, itself a Worker in the same account but a different deployment |
| Gatekeepers | context, custom, scheduler, mcp, mcp-portal |
| Relevant flags | `global_fetch_strictly_public` (upstream default) |
## Reproduction
1. Open a fresh chat with an MCP connector bound to a Gadget. Mine is a Gmail MCP server hosted as a Cloudflare Worker in the same account.
2. Ask the agent for your latest email.
3. It stages a tool call for approval. **Approve it.** Approve every subsequent prompt as it appears — the approvals come in a burst, so this is mostly click-through.
4. If the turn completes, ask for another email. Same thing: approve everything.
5. Repeat. After a handful of turns — for me the connection died somewhere between action IDs 8 and 17, depending on how much each turn did — a turn fails with:
```
Error: 503 error code: 1019
```
Earlier in the investigation the same limit also surfaced as a *tool result* rather than an error, when the MCP server hit the ceiling inside its own chain and reported it as text:
```
Status: ok
Text: Error: Subrequest depth limit exceeded. This request recursed through Workers too many
times. This can happen e.g. if you have a Worker or Durable Object that calls other Workers or
objects recursively.
```
Pressing the retry widget shows the same error each time.
6. **Reload the page.** Do not clear cache, do not start a new chat. Continue the same conversation (you can click the retry widget) — it works again immediately.
Step 6 is the diagnostic. If the limit were per-request, step 5 would never happen; if it were chat or DO state, step 6 would not help.
## Doesn't seem to be a fixed number of model calls
If the root cause were nested worker-to-worker requests (which are documented as capped at 16) within OS, I would expect the failure after exactly 16 requests each time (including small model requests for things like titles). However, this is not the case (as logged by AI Gateway).
Grouping a day of AI Gateway logs into bursts (split on >2 min idle) gives:
```
burst sizes: 1, 2, 17, 21, 10, 16, 17, 16, 21, 16, 13
```
Bursts of 17 and 21 rule out a ceiling of 16 on model calls alone.
Two further observations from the same logs:
- **Every model call succeeds.** All 150 returned `200`. The failure never appears at the gateway — the next call simply never arrives. Whatever is refused is refused before it gets there.
- **Context grows monotonically across bursts** (`tokens_in` climbing 5924 → 14267 with no reset), confirming these are one continuing conversation with reload gaps, not separate chats.
Caveat on the grouping: an idle split is a proxy for a connection boundary, so a long pause mid-session splits one connection into two bursts, and a quick reload may not split at all. So the burst sizes bound the answer loosely rather than measuring it. However, the last burst of 13 was manually verified as a page reload followed by 13 (not 16) requests.
## What I could not find out
I could not observe the failure itself, only its effects.
- `wrangler tail` on `cloudflare-os` and `cloudflare-os-mcp` captured the run-up but not the failure. Two separate attempts were silently truncated after ~60s, both Workers stopping at the same second, which looks like the tail session dropping rather than the Workers going quiet.
- Enabling Workers Traces at `headSamplingRate: 1` did not lead me to it either, though I never managed to read the span tree, so this says more about my tooling than about traces.
- I never found a Worker log line containing `1019`, `503`, or `depth limit`.
I suspect the runtime refuses the invocation before any Worker code runs, which would explain the absence, but I have not shown that.
## Workaround
Reload the page. Everything resumes.
## Version
Reproduced on [`8b08672`](https://github.com/cloudflare/cloudflare-os/commit/8b08672050e6957987a87b5f9cab2801ba89b42c) (2026-08-10, `main` at time of writing).
---
Investigated and drafted with [Claude Code](https://claude.com/claude-code). The reproduction steps are mine and I ran them by hand; the log analysis and the write-up are largely its work. I have checked the claims, and where something is inferred rather than observed the text says so — but flagging the provenance in case it changes how you want to read it.
Contributor guide
Research direction
Start at the /api WebSocket invocation in cloudflare-os and compare the behavior before and after a page reload. Reproduce with wrangler tail or Workers Traces on cloudflare-os and cloudflare-os-mcp, using the AI Gateway logs to correlate calls. Done means identifying the source of the 1019 limit and establishing a concrete fix or confirmed workaround.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- api, backend
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Needs clarification
- Newbie friendliness
- 35/100