Skip to content

bug: approve_or_deny_turn bypasses per-thread lock — race with concurrent chat #33

@G9000

Description

@G9000

Summary

approve_or_deny_turn (service.py:177-317) performs sequence allocation, result persistence, compaction, companion history refresh, and post-turn hooks without acquiring the per-thread asyncio.Lock that _execute_agent_turn uses.

Failure Scenario

  1. User sends a chat message to thread X → acquires get_thread_lock(X)
  2. Simultaneously, user approves a pending tool call on the same thread X → approve_or_deny_turn runs with no lock
  3. Both paths call reserve_message_sequences, persist_agent_result, compact_thread_context, and _refresh_companion_history concurrently
  4. Result: sequence ID collisions, corrupted companion history cache, duplicated or interleaved messages

Fix

Wrap the body of approve_or_deny_turn in async with get_thread_lock(run.thread_id): after resolving the checkpoint and determining the thread ID.

Files

  • apps/server/src/anima_server/services/agent/service.py:177-317

Severity

High — data corruption under concurrent approval + chat on same thread.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions