Skip to content

serve --http: threads with tool-call history fail with 400 missing field name after a runtime restart #5823

Description

@zyxrealhero

Environment

  • codewhale v0.9.11 (Linux), run as codewhale serve --http behind a systemd unit
  • Provider: OpenAI-compatible endpoint (strict serde schema; rejects empty required fields)
  • Client: official integrations/feishu-bridge → /v1/threads/{id}/turns

Symptom
A thread that contains at least one completed tool call works fine while the runtime process stays alive. After the runtime process restarts, any new turn on that thread fails with:

Invalid request (400): Provider API error: Failed to deserialize the JSON body into the target type:
messages[2]: missing field name at line 1 column 1327

Fresh threads (no tool history) work fine after restart. The failure reproduces reliably.

Reproduction

  1. Start codewhale serve --http (any workspace with a tool-capable config).
  2. Create a thread, run a turn that invokes a tool (e.g. read), let it complete.
  3. Restart the serve --http process.
  4. Send another turn on the same thread → 400 missing field name on messages[2].

Captured request body from the failing turn shows the replayed assistant.tool_calls entry is an empty shell:

{
"role": "assistant",
"content": "",
"tool_calls": [
{
"id": "",
"type": "function",
"function": { "name": "", "arguments": "null" }
}
]
}

Root cause (evidence)
Tool metadata (tool id/name/input) is present in the item.started runtime event:

{ "event": "item.started", ...,
"payload": { "item": {...}, "tool": { "id": "call_00_...", "name": "read", "input": {"path": "README.md"} } } }

but the persisted item.completed snapshot only carries summary/detail — the tool fields are dropped. On restart, history is rebuilt from those snapshots, so assistant.tool_calls is serialized with empty id/name and arguments: "null". OpenAI-compatible endpoints that validate required fields then reject the request.

Impact
Any long-lived thread that used tools becomes unusable after every runtime restart (/new is the only workaround). This affects chat bridges (Feishu/Lark/Telegram) that reuse threads across restarts.

Suggested fix
Persist the tool metadata (id, name, input) on the tool-call item snapshot (or reconstruct tool_calls from the event stream on replay) so a restart does not produce empty name/id/arguments.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions