Repository navigation
Description
Before submitting
- I searched existing issues and did not find a duplicate.
- I included enough detail to reproduce or investigate the problem.
Area
apps/server
All times UTC, 2026-11-04. Instance ids are replaced: work-a is the account that hits its limit,
work-b, work-c, work-d and work-e are other Claude accounts of the same user, each its own
claudeAgent provider instance. Thread ids are shortened to <parent>, <p56>, <p8>, <p9>,
<p10b>. Message text is shortened with …. Settings: "Auto-resume limited threads" is on.
Steps to reproduce
What the parent agent did, step by step, from its transcript.
-
The parent runs on
work-aand delegates onwork-a. At 13:50:46 the parent agent called
orchestrator_capabilities. The result lists 13 Claude instances withproviderInstanceId,
displayName,modelsandconstraints, and nothing about usage or limits:{"parentThreadId":"<parent>","inheritedProviderInstanceId":"work-a","inheritedModel":"claude-opus-5-5", …, "providers":[…, {"providerInstanceId":"work-a","driverKind":"claudeAgent","displayName":"Claude Code (work-a)", "models":[…],"canRunChildTask":true,"canRunCrossProviderChildTask":true,"constraints":[]}, …]}From 13:51:35 to 13:51:52 it started four children on the same instance as itself:
delegate_task {"title":"Mobile lane P8: talk to a session","role":"implementation", "target":{"providerInstanceId":"work-a","model":"claude-opus-5-5","options":{"effort":"high"}}, "task":"Read <brief> first and follow it. …"} → {"taskId":"node:delegated-task:…:<p8>", "childThreadId":"<p8>", …}
-
work-ahits its 5-hour limit while everything runs. The children worked for about 30 minutes.
At 14:21:39 to 14:21:46 the parent and all four children failed. The parent's own failed turn showed
the Claude CLI text with a reset time (4:20pm local is 15:20Z):You've hit your 5-hour limit · resets 4:20pmThe parent then received one completion message per child, with no instance and no reset time:
Delegated task node:delegated-task:…:<p8> (Mobile lane P8: talk to a session) [failed]: Claude API rate limit reached. Try again later.The child's own failed item had more (read from
statev2.sqlite,turn-item.updated14:21:41):{"type":"error","title":"Usage limit reached","failure":{"class":"usage_limit", "message":"Claude API rate limit reached. Try again later.","code":"api_error_429", "retryable":true,"resetAt":"2026-11-04T15:20:00.000Z"}}About 3 s later the server armed a resume for each child (
limit-arm:command receipts at
14:21:39 to 14:21:44). -
The user switches the parent thread to the provider instance
work-b. The parent's next run (14:22:16)
ran onwork-band completed. The four children stayed onwork-a. -
The user asks: "pls resume the agents". At 14:22:32 to 14:22:43 the agent sent one message to each
child. It did not change the instance, because nothing told it that the child would run onwork-a
again:t3_thread_send {"threadId":"<p8>","mode":"auto","message":"Resume your lane (p8) where the rate limit stopped you. …"} → {"threadId":"<p8>","runId":"…:ordinal:2","taskId":"…","status":"starting","delivery":"started"}
Each new run started on
work-aand failed in about 1 s (p8 run 2: 14:22:36.912 to 14:22:37.922):Delegated task node:delegated-task:…:thread-send:… (Mobile lane P8: talk to a session) [failed]: Claude API rate limit reached. Try again later. -
The agent restarts the work instead of resuming it. It ran
tools ai usage limits(a local tool
outside T3 Code) to find accounts with room, and at 14:23:29 to 14:23:45 started four NEW
delegate_taskchildren onwork-dandwork-e. These children had none of the old context. The
user objected at 14:30:48: "i didnt want you to restart them but to RESUME THEM". The agent cancelled
three of the copies withtask_cancel. -
The user moves the parent again, to
work-c(parent run at 14:32:29 onwork-c), and says:
"ok now i changed the account to ". The agent read the children's configuration, which still
showedwork-a:t3_thread_configure {"threadId":"<p8>"} → {"threadId":"<p8>","modelSelection":{"instanceId":"work-a","model":"claude-opus-5-5", "options":[{"id":"fastMode","value":false},{"id":"effort","value":"high"}]},"runtimeMode":"full-access", …}
It still sent the resume messages unchanged (14:33:10 to 14:33:23). All four failed in about 1 s
again (p8 run 3: 14:33:15.169 to 14:33:16.268).t3_thread_readon a child at 14:33:50 shows
"status":"failed","providerInstanceId":"work-a"and three failed runs, but no failure class, no reset
time and no armed resume. -
A fresh probe on
work-aalso fails at once (14:34:01,delegate_taskwithmode:"wait"and
"Reply with the single word OK"). Nobody touched it, and at 15:20:01 the armed resume ran it and it
answered "OK". -
The user asks: "isnt there like a delegate mcp tool that let you select the provider on resume?"
Only then did the agent move each child and resend:t3_thread_configure {"threadId":"<p8>","modelSelection":{"instanceId":"work-c","model":"claude-opus-5-5","options":{"effort":"high"}}} → {"sequence":141507} t3_thread_send {"threadId":"<p8>","mode":"auto","message":"Resume your lane (p8) now; the account limit is fixed. …"}
All four children then ran on
work-candwork-dwith their full history and completed.
Expected
- When the parent hears that a child failed, it learns that the failure is a usage limit, on which
instance, when it resets, and that T3 Code will resume the child by itself at that time. - The result, or the
t3_thread_sendanddelegate_taskdescriptions, say thatt3_thread_configure
moves a failed child to another instance and keeps its history. - Optional: when the user moves the parent to another account, the agent can see that its children
are still on the old one.
Actual
- The parent only gets "Claude API rate limit reached. Try again later." The child's failed item has
class,retryableandresetAt, and the server arms a resume, but the MCP results show none of it
(limitRecoveryandusageLimitResetAtdo not appear underapps/server/src/mcp). - The agent retried the limited instance twice for each of the four children (8 runs that failed in
about 1 s), then started four restarted copies without context, before the user pointed it at a tool
that moves a thread.
How to test
apps/server/src/mcp/TaskCancelUsageLimit.integration.test.ts already drives a delegated child into a
usage_limit failure with a resetAt. A new row or case there can assert that the parent's delegated
result (and task_status) carries the instance id, the failure class and resetAt, and that the
child's t3_thread_send after a t3_thread_configure to another instance runs on the new instance while
the armed resume is dropped.
Impact
Major degradation or frequent failure. With several accounts, a usage limit stops every delegated child,
and the agent wastes runs on the limited account or restarts work without its context.
Version or commit
Desktop and server 0.1.5. Source read at main @ 9e85538.
Environment
macOS 27.2, T3 Code desktop 0.1.5, Claude Code 2.1.314 (claudeAgent driver), 13 Claude provider
instances (one per account), model claude-opus-5-5, "Auto-resume limited threads" on.
Workaround
t3_thread_configure the failed child to an instance with room, then t3_thread_send. The child keeps its
full history. The agent must find a free instance outside T3 Code.
Suggested fix
Three parts. The first one alone would have prevented the wasted runs.
1. Tell the parent what happened and what T3 Code already does. Carry the failure class, the instance
id, resetAt and the armed resume into the delegated completion message, task_status and
t3_thread_read. For example:
Usage limit on Claude Code (work-a), 5-hour window. T3 Code resumes this task at 15:20Z.
To continue now on another instance: t3_thread_configure with a new modelSelection, then t3_thread_send.
The data exists: the failed item's failure (class, retryable, resetAt) and the thread's
limitRecovery, which UsageLimitRecoveryWorker
arms a few seconds after the failure. Nothing under apps/server/src/mcp reads either of them today.
2. Say it in the tool descriptions. The delegate_task, task_status
and t3_thread_send descriptions do not say that t3_thread_configure moves a failed child to another
instance and keeps its history. One sentence there would have saved this run. Per AGENTS.md, a change to
this text comes with an eval case in evals/.
3. Show usage state in orchestrator_capabilities, and use it in delegate_task.
- Today the result has, per provider instance:
providerInstanceId,driverKind,displayName,
models,omittedModels,runtimeModes,canRunChildTask,canRunCrossProviderChildTask,
constraints. Nothing says that an instance is at a limit. The agent pickedwork-afor all four
children whilework-awas close to its limit, and later had to run a tool outside T3 Code
(tools ai usage limits) to find an account with room. - The server already has the data and the logic:
- every provider in the registry carries
usageLimits(ServerProviderUsageLimits):
windows withkind(session, weekly, monthly),usedPercent,resetsAt, pluscheckedAtand
unavailable; exhaustedUsageWindowanswers "is this instance
at a limit for this model now";resolveNewThreadModelSelectionfalls back to the
project's fallback model when the default one is exhausted. The web new-thread flow, the mobile
new-task flow, text generation and the MCP project thread creation
use it.delegate_taskandOrchestratorMcpService.capabilities
do not.
- every provider in the registry carries
- Proposal:
- add a small
usageobject per provider in the capabilities result, for example
{"exhausted": true, "window": "session", "usedPercent": 100, "resetsAt": "2026-11-04T15:20:00Z", "checkedAt": "…"},
or{"unknown": "probeFailed"}when there is no reading. Keep it to the exhausted or most-used window, so the
result stays small with many instances; - when
delegate_tasktargets an exhausted instance, say so in its result (or refuse with the reset
time), instead of starting a run that fails in about 1 s.
- add a small
- Limit: for Claude instances the windows can be empty today, see Claude usage limits never populate: rate_limit_event numbers live under unifiedWindows, and get_usage returns rate_limits: null #30310 and [Bug]: Claude instances authenticated with CLAUDE_CODE_OAUTH_TOKEN (claude setup-token) never appear in Usage → Limits, although their sessions stream usable rate-limit windows #24114. Part 3 helps fully
only after those. Parts 1 and 2 do not depend on them, because the failure already carriesresetAt.
Related issues
- Claude usage limits never populate: rate_limit_event numbers live under unifiedWindows, and get_usage returns rate_limits: null #30310 Claude usage limits never populate. Affects part 3 only.
- [Bug]: Claude instances authenticated with CLAUDE_CODE_OAUTH_TOKEN (claude setup-token) never appear in Usage → Limits, although their sessions stream usable rate-limit windows #24114 Claude instances authenticated with
CLAUDE_CODE_OAUTH_TOKENnever appear in Usage → Limits.
Affects part 3 only. - [Bug]: CLIProxy reset times are available in Usage but missing from thread recovery; queued messages block snoozing #26574 CLIProxy reset times are available in Usage but missing from thread recovery. Same family
(reset data that does not reach the place that needs it), different path. - Not a duplicate of any of these: they are about reading the limits. This report is about a delegated
child's limit failure that never reaches the parent agent.
Pull request
I can open a PR for part 1 (and part 2 with its eval case) once you agree on the direction. Part 3
touches the MCP contract, so I would wait for your call on its shape.
Investigated and written by Claude Opus 5.5 in Claude Code, driven from T3 Code. Instance names and
thread ids are anonymized.
Activity