Skip to content

core: utility-gated tool_use leaves orphaned SQLite entry — WARN flood on next session #3168

@bug-ops

Description

@bug-ops

Description

When the utility gate intercepts a tool call (Retrieve/Respond/Verify/Stop action), the LLM assistant message containing the tool_use block is saved to SQLite via persist_message. The matching user message with the ToolResult ([skipped]/[stopped] text) should also be saved via save_only, but on the next session startup the orphan repair in sanitize_tool_pairs fires and emits WARN for the same tool_use ID.

Reproduction Steps

  1. Run a session where the utility gate intercepts a tool call (e.g., invoke_skill with tools: invoke_skill still blocked by utility gate — #3150 only exempted adversarial gate #3163 active: utility scores it above threshold and returns Retrieve action)
  2. Exit the session normally
  3. Start a new session with the same conversation
  4. Observe:
WARN zeph_core::agent::persistence: stripping orphaned mid-history tool_use parts from assistant message tool_ids={"call_vPsWTzWOGWyIb29CukSynS6v"} index=7
WARN zeph_core::agent::persistence: stripping orphaned mid-history tool_result parts from user message tool_use_ids={"call_vPsWTzWOGWyIb29CukSynS6v"} index=8
WARN zeph_core::agent::persistence: skipped 2 empty/orphaned message(s) from history

Expected Behavior

No orphaned tool pairs in history after a utility-gated session. If a tool_use is saved, its matching tool_result must also be loadable on the next startup.

Actual Behavior

The sanitize_tool_pairs repair fires on session startup, emitting 3 WARN messages and soft-deleting the orphaned message pair from SQLite.

Environment

  • Version: v0.19.1 (HEAD 595d355)
  • Features: full
  • Config: .local/config/testing.toml

Logs / Evidence

First observed in CI-571 session.log. The orphaned tool_use ID call_vPsWTzWOGWyIb29CukSynS6v corresponds to an invoke_skill call that was intercepted by the utility gate (Retrieve action) in the previous session.

Root cause hypothesis: the tool_use message is saved by the assistant turn persist path, but the matching user tool_result message may be saved with a Retrieve-action system hint injected between them — causing orphaned_tool_use_ids() to see a System message as next_msg instead of the User tool_result message.

Related: #3163 (invoke_skill blocked by utility gate — the proximate cause of the intercepted call)

Metadata

Metadata

Assignees

Labels

P2High value, medium complexitybugSomething isn't workingmemoryzeph-memory crate (SQLite)

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions