Skip to content

Concurrent biorouter run --no-session invocations collide on shared messages DB (UNIQUE constraint failed: messages.session_id, messages.msg_uid) #31

Description

@Broccolito

Summary

Running two biorouter run --no-session invocations concurrently causes one of them to abort with a SQLite uniqueness error on the shared messages store:

Error: error returned from database: (code: 2067) UNIQUE constraint failed: messages.session_id, messages.msg_uid

Despite passing --no-session, the concurrent runs appear to share (and race on) a common messages/session database, and one run loses the race. The aborted run produces empty/invalid output (no assistant text, 0 tool calls), so with --output-format json the emitted document is effectively empty.

Environment

  • biorouter 1.88.3
  • macOS (Apple Silicon)
  • Invocation: headless biorouter run (no TUI), --no-session, --output-format json

Reproduction

Launch two biorouter run --no-session processes at the same time (here with two different providers/models, but the providers are incidental):

# Terminal A / background job 1
biorouter run --no-session --quiet --output-format json \
  --provider versa_azure --model gpt-5.5-2026-04-24 --max-turns 50 \
  -t "…prompt A…" > a.json 2> a.log &

# Terminal B / background job 2 (started concurrently)
biorouter run --no-session --quiet --output-format json \
  --provider versa_bedrock --model us.anthropic.claude-opus-4-8 --max-turns 50 \
  -t "…prompt B…" > b.json 2> b.log &

Observed

  • One run completes normally.
  • The other aborts early; its .log (stderr) contains:
    Error: error returned from database: (code: 2067) UNIQUE constraint failed: messages.session_id, messages.msg_uid
  • The aborted run's JSON output is empty/unparseable; the run reports success: false with 0 tool calls.
  • Running the same two commands sequentially (one after the other) both succeed. The failure only occurs when they overlap in time.

Expected

--no-session runs should not persist to — or contend on — a shared session/messages database, and concurrent headless invocations should be isolated from one another (e.g., per-process/in-memory storage, or unique per-run session ids). At minimum, a lost race on the shared store should be retried or surfaced as a clear, non-fatal condition rather than silently emptying the output.

Impact

This makes it unsafe to run headless biorouter run jobs in parallel (e.g., fanning out a benchmark across models). Users must serialize all biorouter run invocations, even with --no-session. The failure mode is also easy to miss because the run still exits and writes an (empty) output file.

Likely cause (speculation, not a fix)

The (session_id, msg_uid) UNIQUE constraint on a messages table suggests that:

  • --no-session still writes conversation messages to a shared on-disk DB, and/or
  • concurrent runs derive the same session_id (a default/placeholder rather than a per-run unique id), so their message inserts collide, and/or
  • msg_uid generation isn't unique/atomic across concurrent processes.

Filing as a bug / enhancement; not attempting a fix here — just documenting the behavior and reproduction.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions