Codex pre-sampling compaction fails when custom_tool_call history uses fc_ IDs
- Dominant language
- TypeScript
- Stars
- 2.7k
- Forks
- 401
- Avg merge
- 21h 48m
- Merged PRs (30d)
- 776
Description
## Summary
A long-running Codex session becomes permanently unusable when pre-sampling compaction replays a persisted `custom_tool_call` whose item ID starts with `fc_` instead of `ctc_`.
The live thread works for many turns, but once automatic compaction is triggered, the upstream Responses endpoint rejects the replayed history with HTTP 400.
## Environment
- Cindy: `0.1.74`
- Codex CLI: `0.153.4`
- Model/provider: `gpt-5.6` via `cindy_gateway`
- Platform: macOS arm64
- Observed: 2026-09-08
## Error
```text
Invalid 'input[5].id': 'fc_0d4928f51d034f5e016a9fb59ad1ec87d0ac326c5ecf5a8e36'. Expected an ID that begins with 'ctc'.
```
The app-server logs the terminal failure as:
```text
ERROR codex_core::session::turn: Failed to run pre-sampling compact
```
## Evidence
The persisted rollout item is:
```json
{
"type": "custom_tool_call",
"id": "fc_0d4928f51d034f5e016a9fb59ad1ec87d0ac326c5ecf5a8e36",
"status": "completed",
"call_id": "call_Vtr7eYLfTJKQ6JaXLpgDJaJl",
"name": "exec"
}
```
In the affected rollout, all 97 persisted `custom_tool_call` items use an `fc_` ID; none use `ctc_`.
Immediately before the failure, the latest turn used about `207288 / 258400` context tokens. The next turn triggered pre-sampling compaction, which replayed the malformed historical item and failed deterministically. Retrying the same turn produces the same 400.
## Steps to reproduce
1. Start a Codex session using `gpt-5.6` through `cindy_gateway`.
2. Execute custom tools such as `exec` over enough turns to create a long history.
3. Confirm the persisted response items are `custom_tool_call` objects with `fc_` IDs.
4. Continue until Codex triggers pre-sampling compaction, or resume/replay the thread in a way that resubmits history.
5. Observe the upstream 400 requiring an ID beginning with `ctc`.
## Expected behavior
Cindy/Codex should be able to compact or resume the thread without invalidating persisted tool history.
## Suggested fix
Normalize Responses history at the provider/replay boundary:
- Ensure newly persisted `custom_tool_call` item IDs use the correct `ctc_` form.
- Before compaction/resume replay, sanitize legacy `custom_tool_call` IDs that use `fc_`, while preserving `call_id` so tool calls remain paired with `custom_tool_call_output`.
- Add a regression test covering pre-sampling compaction with legacy malformed IDs.
The available logs establish the malformed persisted type/ID pair and the compaction trigger. They do not establish whether LiteLLM originally emitted the wrong item ID or Codex converted the item type while retaining an `fc_` ID.
Contributor guide
Research direction
Start at the provider/replay boundary and the pre-sampling compaction path, then inspect how persisted custom_tool_call IDs and call_id values are replayed. Add a regression test for pre-sampling compaction with legacy fc_ IDs, preserving pairing with custom_tool_call_output. Done means compaction or resume no longer produces the upstream invalid-ID error.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- ai, backend-api-design
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 55/100