OpenHands / OpenHands/software-agent-sdk

[Bug]: TaskManager forces stream=False for sub-agents using ChatGPT subscription

Open
#4,692 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

agent-execution bug llm priority:medium ready-for-dev
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
  1. Configure OpenHands with an OpenAI Codex model using ChatGPT subscription authentication.

  2. Confirm that a normal parent conversation works with the subscription-backed LLM.

  3. Configure/use the stock task tool with a stock or file-based sub-agent. The sub-agent may use model: inherit.

  4. Ask the parent agent to delegate a task to the sub-agent.

  5. Observe that the delegated child fails with:

    Stream must be set to true

  6. Prevent TaskManager from forcing stream=False for the subscription-backed child LLM.

  7. Repeat the same workflow.

  8. 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 TaskManager respect 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=False behavior 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

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.