Summary
sessions_spawn with runtime="subagent" is effectively unusable from any LLM provider that auto-materializes optional schema fields (OpenAI strict-mode function calling, the GPT-5 family, OpenAI-compatible proxies that normalize tool schemas). Every call gets hard-rejected with:
streamTo is only supported for runtime=acp; got runtime=subagent
even when the agent explicitly does not want to set streamTo.
The issue is a structural interaction between the schema shape and a validator that treats "field present" as "user intent". It is not a user error and it cannot be worked around at the prompt layer.
Root cause
SessionsSpawnToolSchema.streamTo is defined as a single-value enum:
// dist/openclaw-tools-Co0S582U.js:7336
const SESSIONS_SPAWN_ACP_STREAM_TARGETS = ["parent"];
// dist/openclaw-tools-Co0S582U.js:7391
streamTo: optionalStringEnum(SESSIONS_SPAWN_ACP_STREAM_TARGETS),
Providers that strict-materialize optional fields (OpenAI strict mode, GPT-5.x, and OpenAI-compatible proxies) emit every optional field on every tool call. With a one-value enum, there is literally no value the model can pick that means "I don't want this field" — the only valid enum value is "parent", so streamTo always arrives as "parent".
The validator at dist/openclaw-tools-Co0S582U.js:7430-7433 then hard-rejects:
if (streamTo && runtime !== "acp") return jsonResult({
status: "error",
error: `streamTo is only supported for runtime=acp; got runtime=${runtime}`
});
Result: sessions_spawn(runtime="subagent") is impossible from these providers.
Reproduction
Using any OpenAI-compatible provider with auto-fill/strict function calling, have an agent call sessions_spawn with runtime="subagent". The agent will emit arguments like:
{
"task": "...",
"runtime": "subagent",
"streamTo": "parent",
"lightContext": true,
"resumeSessionId": "",
"attachAs": {"mountPath": ""}
}
Observe the {"status":"error","error":"streamTo is only supported for runtime=acp; got runtime=subagent"} response.
Evidence this is not model misbehavior
In a reproducing session on openclaw 2026.4.10, the agent's thinking content explicitly says "retry without streamTo", and its user-facing reply says "calling again without that field", yet the next tool-call arguments still contain streamTo: "parent". This rules out model intent as the cause — the field is being materialized by the provider's schema-filling layer downstream of the model's reasoning.
Cross-check with another tool in the same session: lcm_grep calls also ship since: "" and before: "" for date filters the agent never intended to set. Same auto-fill pattern, different tool. The agent literally cannot express absence of an optional field with this provider.
Over two sessions we measured: first session looped 33 times on the sessions_spawn error before giving up, a second session looped 6 more times after a /reset, a third session looped 4 more times with explicit "do not set streamTo" gotcha documentation loaded at bootstrap. 43 failed calls, zero successes.
Proposed fix
Accept streamTo silently when runtime !== "acp" instead of hard-rejecting. The intent of the guard is to prevent semantic misuse, but with a single-value enum that no caller can avoid materializing, the guard only prevents all use.
Minimal patch (shown against the dist file for clarity — the actual edit should be in src/agents/tools/sessions-spawn.ts):
- const streamTo = params.streamTo === "parent" ? "parent" : void 0;
+ const requestedStreamTo = params.streamTo === "parent" ? "parent" : void 0;
+ const streamTo = runtime === "acp" ? requestedStreamTo : void 0;
const lightContext = params.lightContext === true;
if (runtime === "acp" && lightContext) throw new Error("lightContext is only supported for runtime='subagent'.");
// ... timeoutSecondsCandidate / runTimeoutSeconds / thread / attachments ...
- if (streamTo && runtime !== "acp") return jsonResult({
- status: "error",
- error: `streamTo is only supported for runtime=acp; got runtime=${runtime}`
- });
This keeps streamTo="parent" fully functional for runtime="acp" (the only runtime that actually supports it — the ACP branch at line 7438 is byte-identical after the patch) and silently drops it for runtime="subagent", which is the only way the field can ever arrive from auto-fill providers.
Note: resumeSessionId (line 7434-7437) has the same guard shape but does not have the same auto-fill problem — it's a free-form string and "" is the auto-filled default, which is falsy and does not trigger the guard. Leaving that validator alone is intentional.
Additional concerns
-
Schema field has no description. Unlike lightContext ("Only applies to runtime='subagent'") and resumeSessionId ("Requires runtime='acp'"), streamTo ships without any description. Providers that do respect optionality still won't know the rule. Even after the validator is softened, adding a description would help those providers:
streamTo: optionalStringEnum(SESSIONS_SPAWN_ACP_STREAM_TARGETS, {
description: "Only applies to runtime='acp'. When 'parent', forwards child-run progress to the requester session as system events."
}),
-
Single-value enums in optional tool-schema fields are a general footgun for any tool consumed by strict-mode providers. Consider an internal lint/test that flags single-value enums in tool parameter schemas so this class of bug cannot recur silently.
Environment
openclaw 2026.4.10
- macOS (
Darwin 25.2.0)
- Provider: OpenAI-compatible proxy → GPT-5 family model
- Context: main agent session with
runtime="subagent" spawn attempts
Workaround (local)
For anyone hitting this before a fix lands, a patch script for the minified dist file is included in the reproduction workspace. It needs root (/usr/local/lib/node_modules/...), restarts the gateway, and has to be re-applied on every npm i -g openclaw upgrade — it is a temporary unblock, not a fix.
Summary
sessions_spawnwithruntime="subagent"is effectively unusable from any LLM provider that auto-materializes optional schema fields (OpenAI strict-mode function calling, the GPT-5 family, OpenAI-compatible proxies that normalize tool schemas). Every call gets hard-rejected with:even when the agent explicitly does not want to set
streamTo.The issue is a structural interaction between the schema shape and a validator that treats "field present" as "user intent". It is not a user error and it cannot be worked around at the prompt layer.
Root cause
SessionsSpawnToolSchema.streamTois defined as a single-value enum:Providers that strict-materialize optional fields (OpenAI strict mode, GPT-5.x, and OpenAI-compatible proxies) emit every optional field on every tool call. With a one-value enum, there is literally no value the model can pick that means "I don't want this field" — the only valid enum value is
"parent", sostreamToalways arrives as"parent".The validator at
dist/openclaw-tools-Co0S582U.js:7430-7433then hard-rejects:Result:
sessions_spawn(runtime="subagent")is impossible from these providers.Reproduction
Using any OpenAI-compatible provider with auto-fill/strict function calling, have an agent call
sessions_spawnwithruntime="subagent". The agent will emit arguments like:{ "task": "...", "runtime": "subagent", "streamTo": "parent", "lightContext": true, "resumeSessionId": "", "attachAs": {"mountPath": ""} }Observe the
{"status":"error","error":"streamTo is only supported for runtime=acp; got runtime=subagent"}response.Evidence this is not model misbehavior
In a reproducing session on
openclaw 2026.4.10, the agent's thinking content explicitly says "retry withoutstreamTo", and its user-facing reply says "calling again without that field", yet the next tool-call arguments still containstreamTo: "parent". This rules out model intent as the cause — the field is being materialized by the provider's schema-filling layer downstream of the model's reasoning.Cross-check with another tool in the same session:
lcm_grepcalls also shipsince: ""andbefore: ""for date filters the agent never intended to set. Same auto-fill pattern, different tool. The agent literally cannot express absence of an optional field with this provider.Over two sessions we measured: first session looped 33 times on the
sessions_spawnerror before giving up, a second session looped 6 more times after a/reset, a third session looped 4 more times with explicit "do not set streamTo" gotcha documentation loaded at bootstrap. 43 failed calls, zero successes.Proposed fix
Accept
streamTosilently whenruntime !== "acp"instead of hard-rejecting. The intent of the guard is to prevent semantic misuse, but with a single-value enum that no caller can avoid materializing, the guard only prevents all use.Minimal patch (shown against the dist file for clarity — the actual edit should be in
src/agents/tools/sessions-spawn.ts):This keeps
streamTo="parent"fully functional forruntime="acp"(the only runtime that actually supports it — the ACP branch at line 7438 is byte-identical after the patch) and silently drops it forruntime="subagent", which is the only way the field can ever arrive from auto-fill providers.Note:
resumeSessionId(line 7434-7437) has the same guard shape but does not have the same auto-fill problem — it's a free-form string and""is the auto-filled default, which is falsy and does not trigger the guard. Leaving that validator alone is intentional.Additional concerns
Schema field has no
description. UnlikelightContext("Only applies to runtime='subagent'") andresumeSessionId("Requires runtime='acp'"),streamToships without any description. Providers that do respect optionality still won't know the rule. Even after the validator is softened, adding a description would help those providers:Single-value enums in optional tool-schema fields are a general footgun for any tool consumed by strict-mode providers. Consider an internal lint/test that flags single-value enums in tool parameter schemas so this class of bug cannot recur silently.
Environment
openclaw2026.4.10Darwin 25.2.0)runtime="subagent"spawn attemptsWorkaround (local)
For anyone hitting this before a fix lands, a patch script for the minified dist file is included in the reproduction workspace. It needs root (
/usr/local/lib/node_modules/...), restarts the gateway, and has to be re-applied on everynpm i -g openclawupgrade — it is a temporary unblock, not a fix.