Skip to content

[Bug] Task tool loses session ID (task_id) when sub-agent fails or is cancelled — primary agent cannot recover #13910

@diegonix

Description

@diegonix

Description

When the primary agent dispatches work to a sub-agent via the task tool, the sub-agent's session.id (task_id) is only returned on the success path. If the sub-agent crashes, times out, or is cancelled by the user, SessionPrompt.prompt() throws and the task_id is never communicated back to the calling agent.

This makes recovery impossible: the primary agent has no "handle" to reuse or inspect the failed sub-agent session.

Root Cause

In packages/opencode/src/tool/task.ts (≈ lines 128–162):

// No try-catch around this call:
const result = await SessionPrompt.prompt({
  sessionID: session.id,
  // ...
})

// task_id is only included in the SUCCESSFUL return:
const output = [
  `task_id: ${session.id} (for resuming to continue this task if needed)`,
  "",
  "<task_result>",
  text,
  "</task_result>",
].join("\n")

return { title: params.description, metadata: { sessionId: session.id, model }, output }

If SessionPrompt.prompt() throws (cancellation, timeout, LLM error, etc.), the exception propagates without any session.id context. The primary agent receives a generic "Tool execution aborted" error with no way to reference or resume the sub-session.

Impact

No recovery: The LLM cannot call task with reuse: <task_id> because it never received the ID.
Wasted work & cost: All tokens/actions from the failed sub-agent are invisible and unreachable.
Related symptoms: Issues #9003, #6573, and the "Explore subagent hangs indefinitely" report all describe scenarios where the parent agent is stuck after a sub-agent failure — part of the reason recovery is impossible is this missing task_id.

Suggested Fix

Wrap the SessionPrompt.prompt() call in a try-catch and always return the session.id, even on error:

let result
try {
  result = await SessionPrompt.prompt({
    sessionID: session.id,
    // ...
  })
} catch (error) {
  return {
    title: params.description,
    metadata: { sessionId: session.id, model },
    output: [
      `task_id: ${session.id} (sub-agent session preserved — reuse to retry/inspect)`,
      "",
      "<task_error>",
      error instanceof Error ? error.message : String(error),
      "</task_error>",
    ].join("\n"),
  }
}

// ... existing success path ...

This ensures the primary agent always has a handle to the sub-session, regardless of outcome.

Environment

OpenCode version: 1.2.x (also affects 1.0.x and 1.1.x)
Affects all providers and models

Related Issues

#9003 — main agent hangs because of subagent (explore)
#6573 — Sessions hang indefinitely when Task tool spawns subagents via REST API

Plugins

No response

OpenCode version

1.2.0

Steps to reproduce

  1. Open a session with any model (e.g. Claude Opus 4.6) and send a prompt complex enough to trigger the task tool (e.g. "Explore the codebase and explain how authentication works").
  2. While the sub-agent is running, press Esc to cancel execution (or wait for it to hit a timeout / LLM error).
  3. Observe that the primary agent receives "Tool execution aborted" with no task_id in the response.
  4. Ask the primary agent to retry or resume the sub-agent's work — it will acknowledge that it has no session handle to reuse:

"Eu não reutilizei a sessão anterior porque a tentativa terminou com Tool execution aborted e não retornou task_id. Sem task_id, o contrato me impede de reusar."

  1. Navigate to the sub-agent list in the TUI — the sub-agent session exists (it was persisted in the DB), and may even contain useful partial results, but the primary agent is permanently blind to it.

Screenshot and/or share link

No response

Operating System

Ubunu 24.04.4

Terminal

Ghostty 1.2.3

Metadata

Metadata

Assignees

Labels

bugSomething isn't working

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions