Skip to content

[Bug]: Threads launched into another project via t3_thread_launch can't be waited on, read, sent to, or interrupted by the launching thread #15125

Closed

Description

@ycmjason

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

  1. Register two projects, A and B.
  2. In a thread in project A, have the agent call t3_thread_launch with projectId set to B (workspaceStrategy: {type: "root"}, any model, a short message). The launch succeeds and returns a threadId.
  3. From the same project-A thread, call t3_thread_wait, t3_thread_read, t3_thread_send, t3_thread_interrupt and t3_thread_configuration with that threadId, and t3_thread_search for its title.

Expected behavior

The launching thread can follow the thread it just created. At minimum it should be able to wait on it, read its result and interrupt it. Ideally it can also send follow-ups.

t3_thread_launch's own description says to "retain threadId and use t3_thread_read/t3_thread_wait to follow preparation". That instruction can't be followed when projectId points to another project.

If cross-project management is out of scope on purpose, an agent can still start runs that it can never observe or stop. The launch tool should at least say so.

Actual behavior

The launch works and the thread runs in project B. Every follow-up tool returns thread_not_found for the caller's project, and search leaves the thread out. As a result, the launching agent cannot read the result, send a follow-up, or interrupt a long or runaway run. The only option is the sidebar.

The same sequence works when the thread is launched into the caller's own project (launch, wait, read, send, wait, read).

This follows up on #6945 (cross-project launch, closed as delivered by #2829). That proposal also suggested an optional projectId on t3_thread_list / t3_thread_read.

Impact

Minor bug or occasional failure

Version or commit

0.0.46-nightly.20261003.2623 (fed41fa)

Environment

macOS, T3 Code (Nightly) desktop. The calling thread used the Claude provider and the launched thread used the Codex provider. Reproduced on two separate launches.

Logs or stack traces

t3_thread_launch {projectId: <B>, workspaceStrategy: {type: "root"}, message: "..."}
  -> {"threadId":"mcp:<id>","projectId":"<B>","status":"preparing"}
t3_thread_send / t3_thread_wait / t3_thread_read / t3_thread_interrupt {threadId: "mcp:<id>"}
  -> {"_tag":"OrchestratorMcpFailure","code":"thread_not_found","message":"Thread mcp:<id> was not found in project <A>."}
t3_thread_configuration {threadId: "mcp:<id>"}
  -> {"_tag":"OrchestratorMcpFailure","code":"thread_not_found","message":"The thread was not found in the calling project."}
t3_thread_search {query: "<title>"}
  -> {"matches":[]}

Workaround

Manage the launched thread by hand from the sidebar, or start the conversation from inside project B.


Filed by Claude (Opus 5.5) at the user's request, after reproducing the problem.

Activity

  1. juliusmarminge commented on Oct 3, 2026

    @juliusmarminge
    Member

    Note

    Grok responding on behalf of Julius.

    Triage

    Thanks for the clear repro, @ycmjason! This reproduces on current main. It's the follow-up that Discussion #6945 left for later after launch shipped in #2829 (merged), not a duplicate of that discussion.

    What happens

    t3_thread_launch accepts another project's projectId, and a scratch: true launch also lands in a different project. The new thread runs there, but the follow-up tools only look in the calling thread's project:

    • t3_thread_read, t3_thread_wait, t3_thread_send, and t3_thread_interrupt look up the target using the caller's projectId. If it isn't there, they return thread_not_found with Thread <id> was not found in project <caller>.
    • t3_thread_configuration uses the same lookup and reports The thread was not found in the calling project.
    • t3_thread_search drops matches from other projects, so the result is { matches: [] }.
    • t3_thread_list never lists other projects.

    That project boundary is intentional. Thread management stays inside the calling project, and the descriptions for read, send, interrupt, list, and search all say so. The one cross-project exception is a thread the user attached as context, and that's read-only. An agent can't attach a thread itself to widen its own access.

    What's actually wrong

    The launch instructions don't match that boundary. The t3_thread_launch description and the injected orchestration instructions tell the agent to keep the returned threadId and follow it with t3_thread_read and t3_thread_wait. They also say to check t3_thread_list before retrying a lost response. Those calls only work when the new thread stays in the caller's project, which is why your same-project sequence works. For an explicit projectId or a scratch launch they fail.

    That has two consequences:

    • If a launch response is lost, checking t3_thread_list won't show the first thread, so the agent can start a second one.
    • The launching thread can't interrupt a long or runaway run it started.

    Possible directions

    1. Make the launch instructions accurate. t3_thread_launch and the orchestration instructions should say that read, wait, list, send, and interrupt only see the calling project, so a thread launched into another project or into scratch can't be followed or stopped from the launching thread.
    2. Keep the project boundary in general, but let the launching thread wait on, read, and interrupt the specific threads it launched. Send, search, and list would stay project-scoped unless that's explicitly opened up, and user-attached reads would stay as they are.

    A maintainer will decide on the direction. Until then, your workaround is the right one: manage the thread from the sidebar, or start the conversation inside the target project.

  2. added
    bugSomething is broken or behaving incorrectly.
    via-triageFiled through npx t3 triage
    on Oct 3, 2026
  3. ycmjason commented on Oct 3, 2026

    @ycmjason
    Author

    Thanks for the quick triage! I'd vote for direction 2. Starting a run you can't follow or stop is the bigger problem, and fixing the docs alone doesn't solve it.

    Some context on why cross-project management matters to me: some of my projects are tightly coupled, so work in one often needs a follow-up in another. I'd also like one "No project" thread to act as a hub that launches and drives work across all my projects. Direction 2 covers most of that. If fuller cross-project access (send/list, opt-in or permission-gated) is worth considering, I'm happy to open an Ideas discussion for it rather than widen this issue.

  4. kkolodziej7 commented on Oct 3, 2026

    @kkolodziej7

    Hit the same thing today on 0.0.46-nightly.20261003.2623: an orchestrator thread in project A launched a build thread into project B with workspaceStrategy: worktree; the launch succeeded, then t3_thread_wait and t3_thread_read both returned thread_not_found … in project A. The first launched run also failed during provider startup, and the launching thread had no way to see that.

    Launcher-scoped access would be enough for this case: let a thread read / wait on exactly the threads it launched, without widening access to the rest of the other project. The user-attached-context exception works for reads, but it needs the user in the loop for every launched thread.

  5. cameronmolen commented on Oct 4, 2026

    @cameronmolen

    Adding a use case for direction 2, with one extension: t3_thread_send to launched threads.

    I run an orchestrator thread that launches one worker thread per ticket (each in its own worktree via t3_thread_launch + workspaceStrategy) to build a stacked PR chain. After the bottom PR merges, the orchestrator sends each worker an exact rebase command, bottom-to-top, waiting for each to confirm before messaging the next. It also relays operator requests to the workers that own the affected branches.

    Read/wait/interrupt let it observe workers, but restacking and relaying need t3_thread_send. Without it, the orchestrator has to live in the same project as its workers, which rules out running it from a hub thread. Scoping send to threads the caller launched (same rule as read/wait/interrupt) would keep the project boundary for everything else.

  6. AngeloTadeucci commented on Oct 5, 2026

    @AngeloTadeucci
    Contributor

    Hit this today with t3_thread_send, on Windows desktop 0.0.46-nightly.20261003.2632. Adding it because #15550 covers read, wait and interrupt but leaves send out on purpose.

    What happened

    A thread in project A launched a top-level thread into project B with t3_thread_launch and projectId: 43caff20-a17a-488e-9014-0a120eb4313e. The launch returned threadId: mcp:687c5a78-1fe9-4440-9c75-b8236ebe5ec0. A few minutes later the user approved a follow-up instruction, and the launching thread tried to relay it:

    t3_thread_send {threadId: "mcp:687c5a78-1fe9-4440-9c75-b8236ebe5ec0", message: "..."}
      -> {"_tag":"OrchestratorMcpFailure","code":"thread_not_found","message":"Thread mcp:687c5a78-1fe9-4440-9c75-b8236ebe5ec0 was not found in project a1221c99-acfd-46b5-bfca-481fc98bc223."}
    

    The only way through was asking the user to paste the message into the other thread by hand.

    Ask

  7. juliusmarminge commented on Oct 5, 2026

    @juliusmarminge
    Member

    Note

    Grok responding on behalf of Julius.

    Fixed by #15219, which just landed on main. T3 MCP thread tools now resolve a threadId anywhere in the environment instead of only the calling project, so a thread launched into another project can be read, waited on, sent to, interrupted, and configured by the launching thread (still capped by the caller's own runtime and interaction modes). t3_thread_search keeps defaulting to the caller's project but now takes an explicit projectId. Closing; please reopen if it still reproduces on a build that includes #15219.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething is broken or behaving incorrectly.via-triageFiled through npx t3 triage

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions