{"record":{"id":"179c30267a711904","repo":"zeroclaw-labs/zeroclaw","slug":"matrix-partial-draft-state-unavailable","errorCode":null,"errorMessage":"matrix: partial draft state unavailable","messagePattern":"matrix: partial draft state unavailable","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/zeroclaw-channels/src/matrix.rs","lineNumber":487,"sourceCode":"    impl State {\n        /// Create the draft store matching the immutable Matrix stream mode.\n        pub(super) fn for_stream_mode(mode: MatrixStreamMode) -> Self {\n            match mode {\n                MatrixStreamMode::Off => Self::Off,\n                MatrixStreamMode::Partial => Self::Partial(HashMap::new()),\n                MatrixStreamMode::SingleMessage => Self::Single(HashMap::new()),\n                MatrixStreamMode::MultiMessage => Self::Multi(HashMap::new()),\n            }\n        }\n    }\n\n    pub(super) fn insert_partial(\n        state: &mut State,\n        key: DraftKey,\n        draft: PartialDraft,\n    ) -> Result<()> {\n        let State::Partial(drafts) = state else {\n            bail!(\"matrix: partial draft state unavailable\");\n        };\n        drafts.insert(key, draft);\n        Ok(())\n    }\n\n    pub(super) fn partial_for_update<'a>(\n        state: &'a mut State,\n        key: &DraftKey,\n    ) -> Option<&'a mut PartialDraft> {\n        match state {\n            State::Partial(drafts) => drafts.get_mut(key),\n            _ => None,\n        }\n    }\n\n    pub(super) fn take_partial(state: &mut State, key: &DraftKey) -> Option<PartialDraft> {\n        match state {\n            State::Partial(drafts) => drafts.remove(key),","sourceCodeStart":469,"sourceCodeEnd":505,"githubUrl":"https://github.com/zeroclaw-labs/zeroclaw/blob/88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc/crates/zeroclaw-channels/src/matrix.rs#L469-L505","documentation":"Matrix draft bookkeeping keeps a per-room State enum with distinct variants for partial, single-message and multi-message draft modes. insert_partial may only run while the room is in State::Partial; the let-else destructure fails on any other variant and aborts. The error signals a mode mismatch: a partial draft was recorded after the room already moved to (or never entered) partial mode.","triggerScenarios":"Calling insert_partial(state, key, draft) when state is not State::Partial - e.g. the room already began a single-message or multi-message lifecycle, or its state was finalized/cancelled and reset, while a partial-draft path still tries to insert.","commonSituations":"Two draft lifecycles racing on one room (interleaved update/finalize); a state transition that moves the room out of Partial without draining pending partial inserts; tests driving insert_partial against a freshly constructed non-Partial state (the *_for_same_room_concurrency tests pin this).","solutions":["Check or drive the State variant before inserting: only call insert_partial once the room is in Partial mode.","Audit the transition that moved the state out of Partial (finalize/cancel/upgrade) and make it drain pending partials or make the insert path variant-aware.","In tests, construct the state in the Partial variant before calling insert_partial."],"exampleFix":"// before\ndrafts::insert_partial(&mut state, key, draft)?;\n\n// after\nif !matches!(state, State::Partial(_)) {\n    state = State::Partial(Default::default());\n}\ndrafts::insert_partial(&mut state, key, draft)?;","handlingStrategy":"type-guard","validationCode":null,"typeGuard":"fn in_partial_state(state: &State) -> bool {\n    matches!(state, State::Partial(_))\n}","tryCatchPattern":null,"preventionTips":["Give each room's draft lifecycle a single owner so inserts cannot race a finalize or cancel.","Transition the room into the target draft mode and insert within the same critical section.","Use the variant-specific lookup helpers (partial_for_update and friends) so the expected mode is explicit at every call site."],"tags":["matrix","draft","state-machine","enum-variant"],"backgroundTag":"illegal-state-transition","analyzedSha":"88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc","analyzedAt":"2026-08-23T01:07:41.857Z","schemaVersion":2},"datasetVersion":"2026-08-23T08:06:27.607Z"}