Bug type
Behavior bug (incorrect output/state without crash)
Beta release blocker
No
Summary
OpenAI Responses replay can send overlong or malformed call_id values back to the provider after a tool call, causing the next request to be rejected.
Steps to reproduce
- Install
[email protected], which depends on @mariozechner/[email protected], or install @mariozechner/[email protected] directly.
- Use an OpenAI Responses provider path (
openai-responses / openai-codex-responses) with a prior assistant tool call whose internal tool-call id is stored as {call_id}|{response_item_id}.
- Let the stored
call_id or item id contain a long provider/internal id.
- Inspect the next Responses payload generated for replay after the tool result.
Minimal payload-capture repro:
TMP=$(mktemp -d)
cd "$TMP"
npm init -y >/dev/null
npm install @mariozechner/[email protected] >/dev/null
node --input-type=module <<'NODE'
import { streamOpenAIResponses } from '@mariozechner/pi-ai/openai-responses';
const longCallId = 'call_' + 'x'.repeat(120);
const longItemId = 'notfc_' + 'y'.repeat(120);
const toolCallId = `${longCallId}|${longItemId}`;
const model = {
api: 'openai-responses',
provider: 'openai-codex',
id: 'gpt-test',
baseUrl: 'https://example.invalid/v1',
input: ['text'],
output: ['text'],
headers: {},
};
const context = {
systemPrompt: 'test',
messages: [
{
role: 'assistant',
api: 'openai-responses',
provider: 'openai-codex',
model: 'gpt-test',
content: [{ type: 'toolCall', id: toolCallId, name: 'noop', arguments: {} }],
},
{ role: 'toolResult', toolCallId, content: [{ type: 'text', text: 'ok' }] },
{ role: 'user', content: 'continue' },
],
};
const stream = streamOpenAIResponses(model, context, {
apiKey: 'x',
onPayload(params) {
console.log(JSON.stringify(params.input, null, 2));
throw new Error('stop after payload capture');
},
});
for await (const event of stream) {
if (event.type === 'error') console.error(event.error.errorMessage);
}
NODE
Expected behavior
OpenClaw should normalize Responses tool identifiers before replaying them:
function_call.call_id and matching function_call_output.call_id should be valid Responses call ids, not raw overlong internal ids.
function_call.id should be a valid Responses item id and keep the required fc prefix/shape.
- The replayed tool result should be accepted by OpenAI-compatible Responses APIs.
Actual behavior
The generated replay payload includes the raw overlong call id and raw item id:
[
{
"role": "system",
"content": "test"
},
{
"type": "function_call",
"id": "notfc_yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy",
"call_id": "call_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"name": "noop",
"arguments": "{}"
},
{
"type": "function_call_output",
"call_id": "call_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"output": "ok"
},
{
"role": "user",
"content": [
{
"type": "input_text",
"text": "continue"
}
]
}
]
In real provider runs, this class of payload fails when the Responses API validates input[*].call_id / function-call ids.
OpenClaw version
2026.4.27-beta.1
Operating system
Ubuntu 24.04 / Linux
Install method
npm package / npm global beta path; isolated repro installed @mariozechner/[email protected], which [email protected] depends on.
Model
OpenAI Responses / OpenAI Codex Responses style model using tool calls.
Provider / routing chain
OpenClaw -> @mariozechner/pi-ai Responses provider -> OpenAI-compatible Responses endpoint.
Additional provider/model setup details
The repro uses onPayload to stop before network send, so no API credentials or live provider are required. The failure is in request construction/replay: stored tool-call ids are split and re-emitted without robust normalization.
Logs, screenshots, and evidence
$ npm view [email protected] dependencies.@mariozechner/pi-ai
0.70.5
$ npm view @mariozechner/[email protected] version
0.70.5
# Latest @mariozechner/pi-ai still has the same replay shape at 0.70.6:
# openai-responses-shared.js still splits toolCall.id and msg.toolCallId directly,
# and still stores stream function-call ids as `${item.call_id}|${item.id}`.
# Repro output shows overlong call_id and non-fc item id in the generated payload.
Relevant implementation shape in openai-responses-shared.js:
const [callId, itemIdRaw] = toolCall.id.split("|");
...
call_id: callId
...
const [callId] = msg.toolCallId.split("|");
...
call_id: callId
...
id: `${item.call_id}|${item.id}`
Impact and severity
Affected: users of OpenAI Responses / Codex Responses provider paths when a conversation continues after tool use and stored tool ids contain provider/internal ids that exceed Responses validation limits.
Severity: High for affected runs, because the conversation breaks immediately after tool execution even though the tool itself completed.
Frequency: Always reproduced for replay payload construction with the overlong ids shown above.
Consequence: Tool-using conversations fail on the follow-up request with provider-side validation errors such as invalid/overlong input[*].call_id.
Additional information
A local downstream patch fixes this by normalizing Responses call ids and item ids at all three boundaries:
- When converting stored assistant
toolCall.id back into function_call input items.
- When converting
toolResult.toolCallId into function_call_output.call_id.
- When storing streamed Responses
function_call ids as OpenClaw tool-call ids.
The patch keeps ids deterministic, strips unsafe characters, caps length, and ensures Responses item ids have the expected fc prefix shape.
Bug type
Behavior bug (incorrect output/state without crash)
Beta release blocker
No
Summary
OpenAI Responses replay can send overlong or malformed
call_idvalues back to the provider after a tool call, causing the next request to be rejected.Steps to reproduce
[email protected], which depends on@mariozechner/[email protected], or install@mariozechner/[email protected]directly.openai-responses/openai-codex-responses) with a prior assistant tool call whose internal tool-call id is stored as{call_id}|{response_item_id}.call_idor item id contain a long provider/internal id.Minimal payload-capture repro:
Expected behavior
OpenClaw should normalize Responses tool identifiers before replaying them:
function_call.call_idand matchingfunction_call_output.call_idshould be valid Responses call ids, not raw overlong internal ids.function_call.idshould be a valid Responses item id and keep the requiredfcprefix/shape.Actual behavior
The generated replay payload includes the raw overlong call id and raw item id:
[ { "role": "system", "content": "test" }, { "type": "function_call", "id": "notfc_yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy", "call_id": "call_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", "name": "noop", "arguments": "{}" }, { "type": "function_call_output", "call_id": "call_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", "output": "ok" }, { "role": "user", "content": [ { "type": "input_text", "text": "continue" } ] } ]In real provider runs, this class of payload fails when the Responses API validates
input[*].call_id/ function-call ids.OpenClaw version
2026.4.27-beta.1
Operating system
Ubuntu 24.04 / Linux
Install method
npm package / npm global beta path; isolated repro installed
@mariozechner/[email protected], which[email protected]depends on.Model
OpenAI Responses / OpenAI Codex Responses style model using tool calls.
Provider / routing chain
OpenClaw ->
@mariozechner/pi-aiResponses provider -> OpenAI-compatible Responses endpoint.Additional provider/model setup details
The repro uses
onPayloadto stop before network send, so no API credentials or live provider are required. The failure is in request construction/replay: stored tool-call ids are split and re-emitted without robust normalization.Logs, screenshots, and evidence
Relevant implementation shape in
openai-responses-shared.js:Impact and severity
Affected: users of OpenAI Responses / Codex Responses provider paths when a conversation continues after tool use and stored tool ids contain provider/internal ids that exceed Responses validation limits.
Severity: High for affected runs, because the conversation breaks immediately after tool execution even though the tool itself completed.
Frequency: Always reproduced for replay payload construction with the overlong ids shown above.
Consequence: Tool-using conversations fail on the follow-up request with provider-side validation errors such as invalid/overlong
input[*].call_id.Additional information
A local downstream patch fixes this by normalizing Responses call ids and item ids at all three boundaries:
toolCall.idback intofunction_callinput items.toolResult.toolCallIdintofunction_call_output.call_id.function_callids as OpenClaw tool-call ids.The patch keeps ids deterministic, strips unsafe characters, caps length, and ensures Responses item ids have the expected
fcprefix shape.