OpenHands / OpenHands/software-agent-sdk
[Bug]: TaskManager forces stream=False for sub-agents using ChatGPT subscription
Nobody has claimed this yet.
- Dominant language
- Python
- Stars
- 1.1k
- Forks
- 539
- Avg merge
- 1d 19h
- Merged PRs (30d)
- 137
Description
Is there an existing issue for the same bug?
- I have searched existing issues and this is not a duplicate.
Bug Description
TaskManager forces stream=False on delegated sub-agent LLMs. This breaks sub-agents when the parent or effective child LLM uses OpenAI ChatGPT subscription authentication, because the Codex subscription transport requires stream=True.
The parent conversation works normally with the subscription LLM. The failure occurs when the stock task/sub-agent path creates the child.
I reproduced this end-to-end with openhands-sdk 1.42.1. A local minimal patch that only avoids disabling streaming for subscription-backed child LLMs made the exact same workflow complete successfully.
I also inspected the current main branch on 2026-08-28. TaskManager still unconditionally sets stream=False in both places where the child LLM is prepared, while the current LLM layer now exposes requires_streaming specifically for providers that reject non-streaming requests.
Expected Behavior
A sub-agent should work when its effective LLM uses ChatGPT subscription authentication.
TaskManager should continue disabling unnecessary streaming for providers that support non-streaming requests, but it should not override the transport requirement of an LLM for which requires_streaming is true.
Actual Behavior
The parent agent works with ChatGPT subscription authentication, but invoking a sub-agent through the stock task tool fails before useful sub-agent work is performed with:
Stream must be set to true
The underlying conflicting SDK state can also be reproduced deterministically without making an external API call. From an environment with the OpenHands SDK/tools installed, run:
python - <<'PY'
import tempfile
from openhands.sdk import LLM, Agent
from openhands.sdk.conversation.impl.local_conversation import LocalConversation
from openhands.tools.preset import register_builtins_agents
from openhands.tools.task.manager import TaskManager
# Simulate an LLM whose transport requires streaming.
llm = LLM(model="gpt-4o", stream=True)
llm.is_subscription = True
parent = LocalConversation(
agent=Agent(llm=llm, tools=[]),
workspace=tempfile.mkdtemp(),
visualizer=None,
delete_on_close=True,
)
manager = TaskManager()
manager._ensure_parent(parent)
register_builtins_agents()
child = manager._get_sub_agent("general-purpose")
print(
"parent:",
"requires_streaming=", parent.agent.llm.requires_streaming,
"stream=", parent.agent.llm.stream,
)
print(
"child:",
"requires_streaming=", child.llm.requires_streaming,
"stream=", child.llm.stream,
)
PY
On the affected implementation, the relevant result is:
parent: requires_streaming= True stream= True
child: requires_streaming= True stream= False
So the child LLM still identifies itself as requiring streaming, but TaskManager overrides its transport configuration to stream=False.
With a real ChatGPT subscription-backed Codex LLM, that contradictory child configuration reaches the subscription transport and fails with:
Stream must be set to true
This matches the end-to-end failure I reproduced on openhands-sdk 1.42.1. A minimal local patch preventing TaskManager from disabling streaming for the subscription-backed child allowed the same PRE review sub-agent → parent worker → fresh POST review sub-agent workflow to complete successfully.
Steps to Reproduce
-
Configure OpenHands with an OpenAI Codex model using ChatGPT subscription authentication.
-
Confirm that a normal parent conversation works with the subscription-backed LLM.
-
Configure/use the stock task tool with a stock or file-based sub-agent. The sub-agent may use
model: inherit. -
Ask the parent agent to delegate a task to the sub-agent.
-
Observe that the delegated child fails with:
Stream must be set to true
-
Prevent TaskManager from forcing
stream=Falsefor the subscription-backed child LLM. -
Repeat the same workflow.
-
The delegated sub-agent now runs successfully.
In my end-to-end test this was exercised as:
PRE review sub-agent -> parent worker -> fresh POST review sub-agent.
The stock build failed at the first delegated sub-agent invocation. The minimally patched build completed PRE, worker, and POST successfully.
Acceptance Criteria
- A sub-agent using an LLM that requires streaming can complete through the stock task tool.
- ChatGPT subscription-backed parent and child/sub-agent execution works without
Stream must be set to true. - Providers that do not require streaming retain the existing non-streaming sub-agent behavior.
- The behavior is covered by a regression test for an LLM with
requires_streaming=True. - Both child-LLM construction points in
TaskManagerrespect the effective child LLM's streaming requirement.
Installation Method
Self-hosted OpenHands Agent Server / Canvas Docker deployment
If you selected "Other", please specify
No response
SDK Version
openhands-sdk 1.42.1 (E2E reproduced); current main inspected on 2026-08-28 and still contains the unconditional stream=False overrides
Version Confirmation
- I have confirmed this bug exists on the LATEST version of OpenHands SDK
Python Version
No response
Model Name (if applicable)
OpenAI Codex model via ChatGPT subscription authentication
Operating System
Linux
Logs and Error Messages
The delegated sub-agent fails with:
Stream must be set to true
The parent conversation using the same ChatGPT subscription-backed LLM works normally.
The failure occurs when TaskManager creates the delegated child and forces its LLM to stream=False.
Minimal Code Sample
No response
Screenshots and Additional Context
Root cause analysis:
The unconditional stream=False behavior in TaskManager appears to predate ChatGPT subscription support and makes sense for ordinary providers: delegated agents do not need to stream tokens to a parent UI callback.
However, for ChatGPT subscription/Codex, streaming is also a transport requirement rather than only a token-delivery/UI option.
Current main already models this distinction in the LLM layer through LLM.requires_streaming: providers that require streaming are expected to keep it enabled even when there is no on_token callback, with the response stream drained internally.
TaskManager currently does not appear to respect that abstraction. During sub-agent construction it still unconditionally sets stream=False before the agent factory and then forces stream=False again on the effective child LLM after the factory.
Local proof-of-fix:
On SDK 1.42.1 I tested a minimal version-pinned workaround that:
- preserves the existing
stream=Falsebehavior for non-subscription child LLMs; - keeps/enables streaming for subscription-backed child LLMs;
- applies the condition at both places where TaskManager currently forces
stream=False.
The previously failing end-to-end sub-agent workflow then passed without other behavioral changes.
That workaround targets 1.42.1, which predates the current requires_streaming abstraction.
For current main, a more general upstream solution may therefore be for TaskManager to respect the effective child LLM's requires_streaming property rather than special-casing ChatGPT subscription.
Conceptually:
python stream = True if llm.requires_streaming else False
at the points where TaskManager currently unconditionally disables streaming.
This is only a suggested implementation direction; the maintainers may prefer to enforce this invariant elsewhere in the LLM/sub-agent creation path.
A closely related distinction between callback streaming and provider-required transport streaming has already been handled elsewhere in the SDK (for example the subscription/condenser path).
I can provide the exact 1.42.1 local patch or additional E2E details if useful.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start at TaskManager's two child-LLM construction points and inspect how the effective child LLM's requires_streaming value is handled. Run the provided deterministic reproduction with an LLM configured with requires_streaming=True, then add a regression test covering streaming-required and ordinary providers. Done means delegated sub-agents retain required streaming while existing non-streaming behavior remains unchanged.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- python
- Domain
- ai, backend
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 72/100