Hmbown/CodeWhale · error

Runtime Chat binding state uses an unsupported schema

Error message

Runtime Chat binding state uses an unsupported schema

What it means

RelayState::validate rejects persisted Runtime Chat binding state whose schema_version does not match the compile-time STATE_SCHEMA_VERSION constant. The on-disk relay state was written by a different (older or newer) build than the one reading it, so the library refuses to interpret it rather than guessing at field meanings. This is a deliberate hard stop to prevent misreading a format that may have changed shape or invariants.

Solutions

  1. Delete or move aside the stale Runtime Chat state file so a fresh state with the current schema is created
  2. Rebuild/re-run with the same version of the application that wrote the state file
  3. Update STATE_SCHEMA_VERSION in the constructed state to match the current constant (only for tests/tooling, never by editing persisted files in place)
  4. Add a migration path in code if old schemas must be upgraded rather than discarded

Example fix

// before
let state = RelayState { schema_version: 1, .. };
// after
let state = RelayState { schema_version: STATE_SCHEMA_VERSION, .. };
Defensive patterns

Strategy: validation

Validate before calling

fn ensure_schema_current(state: &RelayState) -> Result<()> {
    if state.schema_version != STATE_SCHEMA_VERSION {
        anyhow::bail!(
            "state schema {} != supported {}",
            state.schema_version,
            STATE_SCHEMA_VERSION
        );
    }
    Ok(())
}

Type guard

fn has_current_schema(state: &RelayState) -> bool {
    state.schema_version == STATE_SCHEMA_VERSION
}

Try / catch

match relay.load_state() {
    Ok(state) => state,
    Err(e) if e.to_string().contains("unsupported schema") => {
        // discard stale state and start fresh
        relay.reset_state()?
    }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: Calling persist_state or test persisted_binding_state_rejects_duplicate_or_nonopaque_authority with a RelayState whose schema_version field differs from STATE_SCHEMA_VERSION — typically state loaded from disk that was written by an older binary after a schema bump, or hand-constructed state with a wrong/omitted version.

Common situations: Upgrading or downgrading the application between versions that bumped STATE_SCHEMA_VERSION; restoring relay state files from a backup made by a different build; tests or tooling that construct RelayState literals without updating the version constant.

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/32a58d532087bab7. Report an issue: GitHub.

Appendix: source

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

    /// native start was proven to have rejected before accepting provider work.
    /// This distinguishes a retryable reservation from an already-projected
    /// terminal turn, whose exact replay must remain settled.
    #[serde(default)]
    start_rejected: bool,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum TurnReservationDisposition {
    New,
    Reopened,
    ExistingUnsettled,
    ExistingTerminal,
}

impl RelayState {
    fn validate(&self) -> Result<()> {
        if self.schema_version != STATE_SCHEMA_VERSION {
            bail!("Runtime Chat binding state uses an unsupported schema");
        }
        if let Some(owner) = self.owner_scope_fingerprint.as_deref() {
            validate_fingerprint(owner)?;
        } else if !self.bindings.is_empty() {
            bail!("Runtime Chat binding state has no account owner");
        }
        let mut binding_ids = HashSet::new();
        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)?;

View on GitHub (pinned to 73e0f67d83)