openclaw - ✅(Solved) Fix sessions_spawn(runtime="subagent") can fail because unified schema exposes ACP-only streamTo [1 pull requests, 1 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
openclaw/openclaw#59390Fetched 2026-04-08 02:24:25
View on GitHub
Comments
0
Participants
1
Timeline
0
Reactions
0
Participants

sessions_spawn currently exposes a unified tool schema that includes streamTo, even though streamTo is only valid for runtime="acp".

In GPT/OpenRouter sessions, the model/tool-calling path may emit streamTo:"parent" for runtime:"subagent" calls. The server then rejects the request with:

streamTo is only supported for runtime=acp; got runtime=subagent

This makes sessions_spawn(runtime="subagent") unreliable on some model/provider combinations, even when the caller intent is a plain sub-agent spawn.

Error Message

When attempting a minimal sub-agent spawn, the request fails with:

Root Cause

This creates model/provider-specific instability:

  • Some models may omit streamTo and appear fine.
  • Others may emit it and fail consistently.
  • Users experience this as “GPT tool calling is flaky”, even though the server-side schema design is a major contributing factor.

Fix Action

Fix / Workaround

  • Mark tool/function schemas as strict where supported, and/or
  • Add provider-specific defensive normalization to strip incompatible fields before dispatch

PR fix notes

PR #68397: fix(sessions_spawn): silently strip ACP-only fields for runtime=subagent

Description (problem / solution / changelog)

Summary

sessions_spawn currently hard-errors when runtime="subagent" receives streamTo or resumeSessionId, even though the subagent code path never consumes either field. Schema-strict providers (most notably gpt-5.4 via the OpenAI bridge) auto-fill every advertised tool parameter on every call regardless of the runtime value the model also picks, which makes subagent spawns fail 90%+ of the time for those models.

This PR switches the two ACP-only fields from "reject with error" to "silently drop when runtime!=='acp'". ACP semantics are unchanged — when runtime==='acp' the fields flow through unmodified and the existing ACP spawn logic validates them.

Why the silent-drop approach

  • The subagent branch never reads streamTo / resumeSessionId, so dropping them has no observable effect on the subagent code path.
  • Schema filtering per-runtime would be the cleaner long-term fix but requires reworking the shared tool-schema pipeline (tracked in #59225, #66719). Silent-drop unblocks affected users today without that churn.
  • Hard-erroring loses to a predictable failure mode that many downstream models cannot avoid — the model doesn't know the field is ACP-only because the schema doesn't tell it.

Related issues

Addresses (non-exhaustive):

  • #68275 — sessions_spawn auto-injects streamTo:"parent" for runtime="subagent"
  • #67248 — sessions_spawn(runtime="subagent") fails on 2026.4.14 with ACP-only streamTo under GPT-5.4
  • #64714 — sessions_spawn rejects subagent runtime when streamTo is auto-filled by strict-mode providers
  • #63120 — LLMs pass streamTo for subagent runtime causing 100% spawn failures
  • #61724 — sessions_spawn(runtime="subagent") fails with "streamTo is only supported for runtime=acp"
  • #60965 — sessions_spawn schema allows streamTo for runtime=subagent but execute rejects it
  • #59390 — unified schema exposes ACP-only streamTo to subagent spawns
  • #56193 — sessions_spawn(runtime="subagent") can receive ACP-only streamTo from tool-call bridge
  • #56326 — sessions_spawn exposes ACP-only fields and breaks runtime=subagent with schema-following models
  • #53370 — Make sessions_spawn more forgiving for ACP-only fields in subagent runtime
  • #43556 — Bug: streamTo in sessions_spawn breaks subagent runtime

Test plan

  • Updated existing rejects resumeSessionId without runtime=acp and rejects streamTo when runtime is not "acp" cases to assert the new silent-drop contract: the tool forwards to spawnSubagentDirect without the ACP-only keys and returns no error.
  • Unchanged: runtime=acp path still passes streamTo / resumeSessionId through to spawnAcpDirect (existing tests at lines 177, 226 still cover this).
  • Suggested: add an integration test that round-trips a runtime="subagent" call with streamTo="parent" through the tool-call bridge to catch future regressions at the schema layer.

Changed files

  • src/agents/tools/sessions-spawn-tool.test.ts (modified, +14/-10)
  • src/agents/tools/sessions-spawn-tool.ts (modified, +9/-16)

Code Example

streamTo is only supported for runtime=acp; got runtime=subagent

---

{
  "status": "error",
  "error": "streamTo is only supported for runtime=acp; got runtime=subagent"
}

---

const SessionsSpawnToolSchema = Type.Object({
  task: Type.String(),
  label: Type.Optional(Type.String()),
  runtime: optionalStringEnum(SESSIONS_SPAWN_RUNTIMES),
  agentId: Type.Optional(Type.String()),
  resumeSessionId: Type.Optional(Type.String()),
  model: Type.Optional(Type.String()),
  thinking: Type.Optional(Type.String()),
  cwd: Type.Optional(Type.String()),
  runTimeoutSeconds: Type.Optional(Type.Number({ minimum: 0 })),
  timeoutSeconds: Type.Optional(Type.Number({ minimum: 0 })),
  thread: Type.Optional(Type.Boolean()),
  mode: optionalStringEnum(SUBAGENT_SPAWN_MODES),
  cleanup: optionalStringEnum(["delete", "keep"]),
  sandbox: optionalStringEnum(SESSIONS_SPAWN_SANDBOX_MODES),
  streamTo: optionalStringEnum(ACP_SPAWN_STREAM_TARGETS),
  attachments: Type.Optional(Type.Array(...)),
  attachAs: Type.Optional(Type.Object({ mountPath: Type.Optional(Type.String()) }))
});

---

const streamTo = params.streamTo === "parent" ? "parent" : void 0;
...
if (streamTo && runtime !== "acp") return jsonResult({
  status: "error",
  error: `streamTo is only supported for runtime=acp; got runtime=${runtime}`
});

---

{
  "task": "Reply with exactly: subagent-ok",
  "runtime": "subagent",
  "agentId": "main",
  "mode": "run"
}

---

{
  "streamTo": "parent"
}
RAW_BUFFERClick to expand / collapse

Bug: sessions_spawn(runtime="subagent") can fail on GPT/OpenRouter because unified tool schema exposes ACP-only streamTo

Summary

sessions_spawn currently exposes a unified tool schema that includes streamTo, even though streamTo is only valid for runtime="acp".

In GPT/OpenRouter sessions, the model/tool-calling path may emit streamTo:"parent" for runtime:"subagent" calls. The server then rejects the request with:

streamTo is only supported for runtime=acp; got runtime=subagent

This makes sessions_spawn(runtime="subagent") unreliable on some model/provider combinations, even when the caller intent is a plain sub-agent spawn.

Environment

  • OpenClaw runtime: main agent session
  • Model route observed: GPT via OpenRouter (aiproxy/gpt in this environment)
  • Channel: Feishu group
  • Workspace: /home/jihua/openclaw/workspace

Observed behavior

When attempting a minimal sub-agent spawn, the request fails with:

{
  "status": "error",
  "error": "streamTo is only supported for runtime=acp; got runtime=subagent"
}

This is especially confusing because the public docs for subagents do not list streamTo as a supported subagent parameter.

Code evidence

1) Unified tool schema includes streamTo for all runtimes

File:

  • /usr/lib/node_modules/openclaw/dist/auth-profiles-B5ypC5S-.js#L180350-L180372

Relevant excerpt:

const SessionsSpawnToolSchema = Type.Object({
  task: Type.String(),
  label: Type.Optional(Type.String()),
  runtime: optionalStringEnum(SESSIONS_SPAWN_RUNTIMES),
  agentId: Type.Optional(Type.String()),
  resumeSessionId: Type.Optional(Type.String()),
  model: Type.Optional(Type.String()),
  thinking: Type.Optional(Type.String()),
  cwd: Type.Optional(Type.String()),
  runTimeoutSeconds: Type.Optional(Type.Number({ minimum: 0 })),
  timeoutSeconds: Type.Optional(Type.Number({ minimum: 0 })),
  thread: Type.Optional(Type.Boolean()),
  mode: optionalStringEnum(SUBAGENT_SPAWN_MODES),
  cleanup: optionalStringEnum(["delete", "keep"]),
  sandbox: optionalStringEnum(SESSIONS_SPAWN_SANDBOX_MODES),
  streamTo: optionalStringEnum(ACP_SPAWN_STREAM_TARGETS),
  attachments: Type.Optional(Type.Array(...)),
  attachAs: Type.Optional(Type.Object({ mountPath: Type.Optional(Type.String()) }))
});

2) Execution explicitly rejects streamTo unless runtime is acp

File:

  • /usr/lib/node_modules/openclaw/dist/auth-profiles-B5ypC5S-.js#L180395-L180403

Relevant excerpt:

const streamTo = params.streamTo === "parent" ? "parent" : void 0;
...
if (streamTo && runtime !== "acp") return jsonResult({
  status: "error",
  error: `streamTo is only supported for runtime=acp; got runtime=${runtime}`
});

3) Subagent docs do not advertise streamTo as a subagent parameter

File:

  • /usr/lib/node_modules/openclaw/docs/tools/subagents.md#L71-L96

The documented subagent params include task, label, agentId, model, thinking, runTimeoutSeconds, thread, mode, cleanup, sandbox, etc., but not streamTo.

Likely root cause

This appears to be a schema/tool-calling compatibility bug rather than a subagent-runtime bug:

  1. sessions_spawn uses a single unified schema for both runtime="subagent" and runtime="acp".
  2. That schema exposes ACP-only fields like streamTo to the model.
  3. Some model/provider tool-calling paths (observed here with GPT via OpenRouter) may opportunistically emit optional fields that are present in the schema, even when they are semantically invalid for the chosen runtime.
  4. The server then rejects the call instead of ignoring the irrelevant field.

In short:

The schema invites the model to emit streamTo; the executor then rejects it for subagents.

Why this matters

This creates model/provider-specific instability:

  • Some models may omit streamTo and appear fine.
  • Others may emit it and fail consistently.
  • Users experience this as “GPT tool calling is flaky”, even though the server-side schema design is a major contributing factor.

Suggested fixes

Preferred fix: runtime-specific tool schemas

Expose different tool schemas depending on runtime, e.g. one of:

  • Separate tools (sessions_spawn_subagent, sessions_spawn_acp), or
  • Conditional schema / discriminated union where:
    • runtime="subagent" does not expose streamTo or resumeSessionId
    • runtime="acp" does expose ACP-only fields

This prevents the model from seeing invalid fields in the first place.

Pragmatic fix: ignore incompatible ACP-only fields for subagent runtime

If runtime !== "acp", silently ignore:

  • streamTo
  • resumeSessionId

instead of returning a hard error.

That would make the API more robust to imperfect tool-call generation.

Optional hardening

  • Mark tool/function schemas as strict where supported, and/or
  • Add provider-specific defensive normalization to strip incompatible fields before dispatch

Minimal repro idea

Ask the model to call:

{
  "task": "Reply with exactly: subagent-ok",
  "runtime": "subagent",
  "agentId": "main",
  "mode": "run"
}

On affected GPT/OpenRouter routes, the actual tool call may still include:

{
  "streamTo": "parent"
}

which then triggers the runtime guard.

Expected behavior

A plain runtime="subagent" spawn should succeed without being vulnerable to ACP-only optional fields leaking into the request.

Actual behavior

A runtime="subagent" call may fail because the schema advertises streamTo, and the executor rejects it when present.

extent analysis

TL;DR

The most likely fix for the issue is to implement runtime-specific tool schemas or ignore incompatible ACP-only fields for subagent runtime.

Guidance

  • Identify the source of the streamTo field emission in the model/provider tool-calling path and prevent it from being sent for runtime="subagent" calls.
  • Modify the server-side schema to use runtime-specific tool schemas, exposing different fields depending on the chosen runtime.
  • As a temporary workaround, consider ignoring incompatible ACP-only fields for subagent runtime instead of returning a hard error.
  • Review the documentation for subagents to ensure it accurately reflects the supported parameters and any limitations.

Example

// Example of conditional schema for sessions_spawn
const SessionsSpawnToolSchema = Type.Object({
  task: Type.String(),
  label: Type.Optional(Type.String()),
  runtime: optionalStringEnum(SESSIONS_SPAWN_RUNTIMES),
  agentId: Type.Optional(Type.String()),
  // ...
  streamTo: runtime === "acp" ? optionalStringEnum(ACP_SPAWN_STREAM_TARGETS) : Type.Null(),
});

Notes

The provided fix suggestions assume that the issue is caused by the unified tool schema exposing ACP-only fields to the model. However, the actual root cause may be more complex and require additional investigation.

Recommendation

Apply the pragmatic fix: ignore incompatible ACP-only fields for subagent runtime. This approach provides a more robust API and prevents errors caused by imperfect tool-call generation, while also being a more straightforward solution to implement compared to creating runtime-specific tool schemas.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

FAQ

Expected behavior

A plain runtime="subagent" spawn should succeed without being vulnerable to ACP-only optional fields leaking into the request.

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING

openclaw - ✅(Solved) Fix sessions_spawn(runtime="subagent") can fail because unified schema exposes ACP-only streamTo [1 pull requests, 1 participants]