Summary
When OpenClaw is using the lossless-claw context engine with an OpenAI Responses model, assembled LCM context can re-introduce call_id|fc_id / fc_* function-call identifiers after transcript sanitization has already run.
This can trigger provider-side errors like:
Invalid 'input[86].id': 'fc_VlMFjbiUmP_P7YRExGpwuqcZ3Wxmoy6g522VwS00A3TOSbsgVftCkLT0bdYrd'. Expected an ID that contains letters, numbers, underscores, or dashes, but this value contained additional characters.
Why this happens
OpenClaw already knows about this failure mode:
src/agents/pi-embedded-helpers/openai.ts
downgradeOpenAIFunctionCallReasoningPairs()
- comment says OpenAI can reject replayed
function_call items with fc_* ids when the matching reasoning item is absent
And sanitizeSessionHistory() applies those downgrades before the model call:
src/agents/pi-embedded-runner/google.ts
But in the main run path, contextEngine.assemble() happens after sanitizeSessionHistory():
src/agents/pi-embedded-runner/run/attempt.ts
If lossless-claw returns assembled messages from storage, those messages are not re-sanitized for OpenAI Responses afterwards.
In lossless-claw, the assembler currently only does pairing repair on return:
src/assembler.ts
- returns
sanitizeToolUseResultPairing(rawMessages)
That means OpenAI-invalid replay shapes can be reintroduced by the context engine even if the live session history was already sanitized earlier in the pipeline.
Practical impact
This seems more likely when freshTailCount is large, because more recent raw assistant/tool-call turns are preserved and replayed.
In my case, reducing freshTailCount from 250 to 80 mitigated the issue, but that is just a workaround.
Suggested fix
One of:
- Preferred: In OpenClaw core, re-run the OpenAI transcript sanitizers after
contextEngine.assemble() and before replacing active session messages.
- Or, inside
lossless-claw, avoid returning replayed OpenAI function-call ids that depend on matching reasoning state being present in the same assistant turn.
Possible reproduction
- Use OpenClaw with
lossless-claw as context engine
- Use an OpenAI Responses model
- Have prior assistant turns containing tool call ids shaped like
call_xxx|fc_xxx
- Let LCM assemble those older turns from storage into the prompt
- Provider rejects replayed input because the
fc_* id is no longer valid in the reconstructed context
Notes
I’m filing this against lossless-claw because the context engine is the component reintroducing the problematic shape, but the durable fix may belong in OpenClaw core after assemble().
Summary
When OpenClaw is using the
lossless-clawcontext engine with an OpenAI Responses model, assembled LCM context can re-introducecall_id|fc_id/fc_*function-call identifiers after transcript sanitization has already run.This can trigger provider-side errors like:
Why this happens
OpenClaw already knows about this failure mode:
src/agents/pi-embedded-helpers/openai.tsdowngradeOpenAIFunctionCallReasoningPairs()function_callitems withfc_*ids when the matchingreasoningitem is absentAnd
sanitizeSessionHistory()applies those downgrades before the model call:src/agents/pi-embedded-runner/google.tsBut in the main run path,
contextEngine.assemble()happens aftersanitizeSessionHistory():src/agents/pi-embedded-runner/run/attempt.tsIf
lossless-clawreturns assembled messages from storage, those messages are not re-sanitized for OpenAI Responses afterwards.In
lossless-claw, the assembler currently only does pairing repair on return:src/assembler.tssanitizeToolUseResultPairing(rawMessages)That means OpenAI-invalid replay shapes can be reintroduced by the context engine even if the live session history was already sanitized earlier in the pipeline.
Practical impact
This seems more likely when
freshTailCountis large, because more recent raw assistant/tool-call turns are preserved and replayed.In my case, reducing
freshTailCountfrom 250 to 80 mitigated the issue, but that is just a workaround.Suggested fix
One of:
contextEngine.assemble()and before replacing active session messages.lossless-claw, avoid returning replayed OpenAI function-call ids that depend on matching reasoning state being present in the same assistant turn.Possible reproduction
lossless-clawas context enginecall_xxx|fc_xxxfc_*id is no longer valid in the reconstructed contextNotes
I’m filing this against
lossless-clawbecause the context engine is the component reintroducing the problematic shape, but the durable fix may belong in OpenClaw core afterassemble().