Hmbown/CodeWhale · error

Runtime Chat binding state contains duplicate thread…

Error message

Runtime Chat binding state contains duplicate thread authority

What it means

RelayState::validate enforces uniqueness of thread authority identifiers: runtime_binding_id, virtual_thread_id, and native_thread_id must each be unique across all bindings in the state. A duplicate means two bindings claim the same thread identity, which would make routing ambiguous, so persistence is refused.

Solutions

  1. Remove or regenerate the duplicate binding so each runtime_binding_id, virtual_thread_id, and native_thread_id is unique
  2. Regenerate the virtual thread id (local_thread_ + fresh hex) for the second binding
  3. Deduplicate bindings before constructing RelayState (e.g. with a HashSet keyed on the ids)
  4. Fix the id-generation code that produced a collision

Example fix

// before
state.bindings.push(binding.clone()); // same ids as an existing binding
// after
binding.virtual_thread_id = new_virtual_thread_id();
binding.native_thread_id = new_native_thread_id();
state.bindings.push(binding);
Defensive patterns

Strategy: validation

Validate before calling

fn ensure_unique_authority(state: &RelayState) -> Result<()> {
    let mut seen = std::collections::HashSet::new();
    for b in &state.bindings {
        for id in [&b.runtime_binding_id, &b.virtual_thread_id, &b.native_thread_id] {
            if !seen.insert(id.clone()) {
                anyhow::bail!("duplicate thread authority: {id}");
            }
        }
    }
    Ok(())
}

Type guard

fn bindings_have_unique_authority(state: &RelayState) -> bool {
    let ids: Vec<_> = state.bindings.iter()
        .flat_map(|b| [&b.runtime_binding_id, &b.virtual_thread_id, &b.native_thread_id])
        .collect();
    ids.iter().collect::<std::collections::HashSet<_>>().len() == ids.len()
}

Try / catch

if let Err(e) = relay.persist_state(&state) {
    if e.to_string().contains("duplicate thread authority") {
        // regenerate ids for the offending binding and retry once
    } else { return Err(e); }
}

Prevention

When it happens

Trigger: Calling persist_state (or the persisted_binding_state_rejects_duplicate_or_nonopaque_authority test) with a RelayState containing two bindings that share any of runtime_binding_id, virtual_thread_id, or native_thread_id.

Common situations: Copy-pasting a binding in test fixtures; a merge of two state files that produced overlapping ids; a bug that regenerates the same virtual thread id for two runs; restoring state where a new binding reused an old id.

Understand the failure class

Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.

Related errors


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

Appendix: source

Thrown at crates/tui/src/runtime_chat_relay.rs:231

        let mut virtual_threads = HashSet::new();
        let mut native_threads = HashSet::new();
        for binding in &self.bindings {
            validate_relay_id(&binding.run_id, "run id")?;
            validate_relay_id(&binding.runtime_binding_id, "binding id")?;
            validate_virtual_thread_id(&binding.virtual_thread_id)?;
            validate_native_record_id(&binding.native_thread_id, "native thread id")?;
            validate_route_id(&binding.model_provider, "provider id")?;
            validate_route_id(&binding.model_provider_id, "model-provider id")?;
            validate_model_id(&binding.model)?;
            validate_fingerprint(&binding.first_operation_fingerprint)?;
            if let Some(fingerprint) = binding.system_prompt_fingerprint.as_deref() {
                validate_fingerprint(fingerprint)?;
            }
            if !binding_ids.insert(binding.runtime_binding_id.clone())
                || !virtual_threads.insert(binding.virtual_thread_id.clone())
                || !native_threads.insert(binding.native_thread_id.clone())
            {
                bail!("Runtime Chat binding state contains duplicate thread authority");
            }
            for (virtual_turn_id, turn) in &binding.turns {
                validate_virtual_turn_id(virtual_turn_id)?;
                validate_native_record_id(&turn.native_turn_id, "native turn id")?;
                validate_fingerprint(&turn.operation_fingerprint)?;
                validate_fingerprint(&turn.request_fingerprint)?;
            }
        }
        Ok(())
    }
}

impl RuntimeChatRelayHost {
    pub(crate) fn open(
        config: Config,
        _plugin_registry: Arc<PluginRegistry>,
        private_root: PathBuf,
        target_ref: String,

View on GitHub (pinned to 73e0f67d83)