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
Steps to reproduce
- From an agent thread with the orchestrator MCP tools, call
t3_thread_launchwith the first prompt underpromptinstead ofmessage:{"title": "Repro: prompt instead of message", "scratch": true, "prompt": "say hi"} - Read the new thread with
t3_thread_read.
Expected behavior
The call fails with a validation error naming the unknown key (prompt), ideally pointing at message. Or at least the result carries a warning, so the calling agent knows its brief was dropped.
Actual behavior
The call succeeds and returns "runId": null, "status": null. The thread is created idle, with runCount: 0 and itemCount: 0. The prompt value is silently discarded and nothing tells the caller.
Reproduced on 2026-10-28 with 0.1.4 nightly 20261027.3783.
Impact
Major: a workflow is unreliable.
prompt is the name agents guess naturally. In our provider logs, 14 of 14 launches that passed prompt never started. A launch with a valid message creates a run (78 of 78). One of the silent failures was a scheduled production deploy session that missed its slot. Agents can't tell an unknown key from a valid launch whose run has not been created yet.
Version or commit
0.1.4 nightly 20261027.3783 (Linux desktop). Source read at 9826dbb9904b2591bfbf60d1317486b59132f56b.
Environment
Linux (Arch), desktop app, Claude provider. The calls come over the MCP HTTP server from Claude Code agent threads.
Logs or stack traces
None: no error or warning is logged, which is the problem.
Screenshots, recordings, or supporting files
None.
Workaround
Pass the brief as message. Treat runId: null on a launch with a brief as a failure, and resend it with t3_thread_send.
Cause, from reading the source
ThreadLaunchToolinapps/server/src/mcp/toolkits/project/tools.tsdeclares its parameters asSchema.Struct({...})withmessageoptional and noprompt.- Every MCP tool is registered through
decodeAsAdvertisedinapps/server/src/mcp/McpHttpServer.ts(around line 447):tool.setParameters(Schema.toCodecJson(tool.parametersSchema)). - Effect Schema's default decode option is
onExcessProperty: "ignore", so unknown keys are stripped without an error. - With
messageabsent, the launch takes the "idle thread" path, exactly as the description says ("Omit it andattachmentsfor idle"). It is indistinguishable from an intentional idle launch.
This affects every MCP tool, not just t3_thread_launch. For example, schedule_task with a misspelled field, or t3_thread_send with text instead of message, would be dropped the same way.
Proposed fix
Reject excess keys centrally, where the advertised codec is built:
// apps/server/src/mcp/McpHttpServer.ts, decodeAsAdvertised
tool.setParameters(
Schema.toCodecJson(tool.parametersSchema).annotate({
parseOptions: { onExcessProperty: "error" },
}),
),The same pattern is already used in packages/contracts/src/keybindings.ts (with "ignore") and as a decode option in packages/provider-core/src/server/textGenerationUtils.ts ("error").
- What callers see: the parse error names the unexpected key, so a model can correct itself on the next call.
- What the schema advertises: if the JSON Schema output then emits
additionalProperties: false,advertisedJsonSchemakeeps it (compact()only stripsadditionalProperties: true), and clients see it in the advertised schema too. - Gentler alternative: if strictness risks breaking clients that send extra keys, keep
"ignore"but havet3_thread_launch(andt3_thread_send) return awarningsentry whenprompt,textorcontentarrives withoutmessage.
I have not run the test suite against this change. A test in McpHttpServer.test.ts that calls a probe tool with an extra key and expects isError: true would pin it.
Activity
syz51 commentedon Nov 8, 2026
Still reproduces on upstream
mainat8e6a732305(2026-11-08).On 2026-11-08 a coordinator agent passed the brief as
prompttot3_thread_launchfour times. Each call succeeded withrunId: null, and the four threads sat idle until someone noticed.The
t3_thread_launchparameters inapps/server/src/mcp/toolkits/project/tools.tsare a plainSchema.Struct, so the extra key is dropped before the handler runs. Rejecting unknown keys (or advertisingadditionalProperties: falsein the tool's JSON Schema) would turn this into an error the agent can retry withmessage.Guillaume-Lombardo commentedon Nov 8, 2026
Also hit on 0.1.7 (Linux server), and the code path is unchanged on
mainatade473018d. One detail that makes it more costly than an idle thread: with aworktreestrategy, the droppedpromptstill gets a fully provisioned workspace.{ "projectId": "<project>", "title": "Repro", "workspaceStrategy": { "type": "worktree", "baseRef": "origin/main", "branch": "feat/repro" }, "prompt": "Say hello." }The call returns
runId: null, status: null, andThreadLaunchService.launchstill runsschedulePreparation→prepareInBackground(input, threadId, null): the branch and worktree are created, bound withthread.metadata.update, and the setup script runs. With no run, noprepared-run.*item shows up, so the thread looks ready and waiting for its agent. Two occurrences here, one sat for 45 minutes before the caller resent the brief witht3_thread_send.#26721 covers this as far as I can tell, since the unknown-key check runs before the handler, so no thread or worktree gets created.
— Guillaume