Hmbown/CodeWhale · error

operation_key is already bound to a different turn request

Error message

operation_key is already bound to a different turn request

What it means

Turn operation idempotency check: the operation_key's stored binding (thread, key fingerprint, request fingerprint, or requested turn) differs from the incoming request — the same key is being replayed with different content, which would silently alias two different turns.

Solutions

  1. Use a distinct operation_key for the new turn request, or reuse the exact original request for a replay.
  2. Treat the persisted turn as the authoritative result of the earlier operation key.
Defensive patterns

Strategy: validation

When it happens

Trigger: Thrown at crates/tui/src/runtime_threads.rs:9064 when the library encounters an invalid state.

Common situations: See trigger scenarios.


AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22). Data as JSON: /api/errors/675525b1499ddfbd. Report an issue: GitHub.

Appendix: source

Thrown at crates/tui/src/runtime_threads.rs:9064

        &self,
        prepared: &PreparedRuntimeTurnOperation,
    ) -> Result<Option<TurnRecord>> {
        let requested = &prepared.binding;
        let Some(persisted) = self
            .store
            .load_turn_operation_binding(&requested.operation_key_fingerprint)?
        else {
            return Ok(None);
        };
        if persisted.thread_id != requested.thread_id
            || persisted.operation_key_fingerprint != requested.operation_key_fingerprint
            || persisted.request_fingerprint != requested.request_fingerprint
            || prepared
                .requested_turn_id
                .as_deref()
                .is_some_and(|turn_id| persisted.turn_id != turn_id)
        {
            bail!("operation_key is already bound to a different turn request");
        }
        let turn_path = self.store.turn_path(&persisted.turn_id)?;
        if !turn_path.exists() {
            bail!("operation_key binding is incomplete; retry after Runtime recovery");
        }
        let turn = self.store.load_turn(&persisted.turn_id)?;
        if turn.id != persisted.turn_id || turn.thread_id != persisted.thread_id {
            bail!("operation_key binding does not match its persisted Runtime turn");
        }
        Ok(Some(turn))
    }

    /// Read the exact durable binding without replaying, recovering, loading an
    /// engine, or creating even a claim-lock file. The shared claim prevents a
    /// reader from observing admission records that can still be rolled back.
    pub(crate) fn lookup_turn_operation(
        &self,
        thread_id: &str,

View on GitHub (pinned to 73e0f67d83)