{"record":{"id":"32a58d532087bab7","repo":"Hmbown/CodeWhale","slug":"runtime-chat-binding-state-uses-an-unsupported-schema","errorCode":null,"errorMessage":"Runtime Chat binding state uses an unsupported schema","messagePattern":"Runtime Chat binding state uses an unsupported schema","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/tui/src/runtime_chat_relay.rs","lineNumber":205,"sourceCode":"    /// native start was proven to have rejected before accepting provider work.\n    /// This distinguishes a retryable reservation from an already-projected\n    /// terminal turn, whose exact replay must remain settled.\n    #[serde(default)]\n    start_rejected: bool,\n}\n\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\nenum TurnReservationDisposition {\n    New,\n    Reopened,\n    ExistingUnsettled,\n    ExistingTerminal,\n}\n\nimpl RelayState {\n    fn validate(&self) -> Result<()> {\n        if self.schema_version != STATE_SCHEMA_VERSION {\n            bail!(\"Runtime Chat binding state uses an unsupported schema\");\n        }\n        if let Some(owner) = self.owner_scope_fingerprint.as_deref() {\n            validate_fingerprint(owner)?;\n        } else if !self.bindings.is_empty() {\n            bail!(\"Runtime Chat binding state has no account owner\");\n        }\n        let mut binding_ids = HashSet::new();\n        let mut virtual_threads = HashSet::new();\n        let mut native_threads = HashSet::new();\n        for binding in &self.bindings {\n            validate_relay_id(&binding.run_id, \"run id\")?;\n            validate_relay_id(&binding.runtime_binding_id, \"binding id\")?;\n            validate_virtual_thread_id(&binding.virtual_thread_id)?;\n            validate_native_record_id(&binding.native_thread_id, \"native thread id\")?;\n            validate_route_id(&binding.model_provider, \"provider id\")?;\n            validate_route_id(&binding.model_provider_id, \"model-provider id\")?;\n            validate_model_id(&binding.model)?;\n            validate_fingerprint(&binding.first_operation_fingerprint)?;","sourceCodeStart":187,"sourceCodeEnd":223,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/src/runtime_chat_relay.rs#L187-L223","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Delete or move aside the stale Runtime Chat state file so a fresh state with the current schema is created","Rebuild/re-run with the same version of the application that wrote the state file","Update STATE_SCHEMA_VERSION in the constructed state to match the current constant (only for tests/tooling, never by editing persisted files in place)","Add a migration path in code if old schemas must be upgraded rather than discarded"],"exampleFix":"// before\nlet state = RelayState { schema_version: 1, .. };\n// after\nlet state = RelayState { schema_version: STATE_SCHEMA_VERSION, .. };","handlingStrategy":"validation","validationCode":"fn ensure_schema_current(state: &RelayState) -> Result<()> {\n    if state.schema_version != STATE_SCHEMA_VERSION {\n        anyhow::bail!(\n            \"state schema {} != supported {}\",\n            state.schema_version,\n            STATE_SCHEMA_VERSION\n        );\n    }\n    Ok(())\n}","typeGuard":"fn has_current_schema(state: &RelayState) -> bool {\n    state.schema_version == STATE_SCHEMA_VERSION\n}","tryCatchPattern":"match relay.load_state() {\n    Ok(state) => state,\n    Err(e) if e.to_string().contains(\"unsupported schema\") => {\n        // discard stale state and start fresh\n        relay.reset_state()?\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Persist STATE_SCHEMA_VERSION in every serialized state and check it right after deserialization","Ship a migration step on upgrade instead of silently reading old state","Never hand-construct RelayState without referencing STATE_SCHEMA_VERSION"],"tags":["schema","persistence","state","validation"],"backgroundTag":"schema-validation-failed","analyzedSha":"73e0f67d83c59909b571efdfc88c4bc28c309cb1","analyzedAt":"2026-09-22T01:30:00.501Z","contentChangedAt":"2026-09-22T01:30:00.501Z","schemaVersion":2},"datasetVersion":"2026-09-22T16:17:23.217Z"}