You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
TaskTool returns no diagnostic context when subagent result text is empty or missing #24447
TaskTool appears to have no diagnostic fallback when a subagent result contains no usable text output. In packages/opencode/src/tool/task.ts, it serializes the last text part and falls back to "" if none is found.
In one local case on 1.14.22, this surfaced in the parent session as an empty <task_result>, which made it unclear whether the subagent completed without text, errored, or produced only non-text parts.
The distinction I’m trying to preserve here is the TaskTool serialization boundary rather than the upstream cause.
This issue is not claiming to explain all empty subagent results, task hangs, or provider-specific text loss. The narrower problem is that this expression:
makes several states indistinguishable to the parent:
a text part exists but is empty
no text part exists
only non-text parts exist
the child result has diagnostic metadata but no text
an upstream runtime/provider/adapter issue produced no usable text
So even if #21250 fixes trailing empty assistant runs, and even if #5204 fixes provider-error propagation, TaskTool can still return a blank successful <task_result> when no usable text part is available.
I think this issue is best treated as diagnostic/serialization hardening: avoid returning a blank <task_result> when the task result has no usable text to serialize.
After looking at the different PRs trying to solve this (#28021, #29176, and #29048), I realized that none of the approaches are perfect in isolation because they don't differentiate between legitimate empty outputs and illegitimate empty outputs.
The 'Fail Fast' approach (fix(tool): trigger fallback on empty task output #29048): Unconditionally throwing an error when there is no text successfully triggers the FallbackProvider (which is necessary for silent failures and rate limits), but it accidentally breaks legitimate use cases (e.g., when a subagent successfully uses a tool but doesn't emit final text).
We need a hybrid approach! The TaskTool should look at the finishReason or the parts array:
If finishReason === 'tool-calls' or it successfully emitted tool parts -> Return the diagnostic string (legitimate zero answer).
If finishReason === 'unknown' or 'error' and the parts array is empty -> Throw an explicit error so the fallback chain can catch it and switch models (illegitimate zero answer).
I will work on updating my PR to implement this combined logic!
Activity
github-actions commentedon Apr 26, 2026
This issue might be a duplicate of or closely related to existing issues. Please check:
If this is distinct from those issues, it would help to clarify what differentiates the root cause or the fix scope.
gmnstr commentedon Apr 26, 2026
I agree these are related.
After checking the linked issues, I think the closest overlaps are:
info.errorcan be downgraded into an empty<task_result>The distinction I’m trying to preserve here is the
TaskToolserialization boundary rather than the upstream cause.This issue is not claiming to explain all empty subagent results, task hangs, or provider-specific text loss. The narrower problem is that this expression:
result.parts.findLast((item) => item.type === "text")?.text ?? ""makes several states indistinguishable to the parent:
So even if #21250 fixes trailing empty assistant runs, and even if #5204 fixes provider-error propagation,
TaskToolcan still return a blank successful<task_result>when no usable text part is available.I think this issue is best treated as diagnostic/serialization hardening: avoid returning a blank
<task_result>when the task result has no usable text to serialize.niStee commentedon May 27, 2026
After looking at the different PRs trying to solve this (#28021, #29176, and #29048), I realized that none of the approaches are perfect in isolation because they don't differentiate between legitimate empty outputs and illegitimate empty outputs.
FallbackProvider(which is necessary for silent failures and rate limits), but it accidentally breaks legitimate use cases (e.g., when a subagent successfully uses a tool but doesn't emit final text).FallbackProvidernever intercepts the failure when it actually is a rate limit or silent crash.We need a hybrid approach! The
TaskToolshould look at thefinishReasonor thepartsarray:finishReason === 'tool-calls'or it successfully emitted tool parts -> Return the diagnostic string (legitimate zero answer).finishReason === 'unknown'or'error'and the parts array is empty -> Throw an explicit error so the fallback chain can catch it and switch models (illegitimate zero answer).I will work on updating my PR to implement this combined logic!
github-actions commentedon Jul 26, 2026
To stay organized issues are automatically closed after 60 days of no activity. If the issue is still relevant please open a new one.