{"record":{"id":"873ab6d3500cd1cc","repo":"zeroclaw-labs/zeroclaw","slug":"matrix-single-message-draft-state-unavailable","errorCode":null,"errorMessage":"matrix: single-message draft state unavailable","messagePattern":"matrix: single-message draft state unavailable","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/zeroclaw-channels/src/matrix.rs","lineNumber":529,"sourceCode":"    pub(super) fn partial_contains(state: &State, key: &DraftKey) -> bool {\n        matches!(state, State::Partial(drafts) if drafts.contains_key(key))\n    }\n\n    #[cfg(test)]\n    pub(super) fn partial_len(state: &State) -> usize {\n        match state {\n            State::Partial(drafts) => drafts.len(),\n            _ => 0,\n        }\n    }\n\n    pub(super) fn insert_single(\n        state: &mut State,\n        key: DraftKey,\n        draft: SingleDraft,\n    ) -> Result<()> {\n        let State::Single(drafts) = state else {\n            bail!(\"matrix: single-message draft state unavailable\");\n        };\n        drafts.insert(key, draft);\n        Ok(())\n    }\n\n    /// Return the editable `single_message` draft for this room+draft id, if it\n    /// is still active.\n    pub(super) fn single_for_update<'a>(\n        state: &'a mut State,\n        key: &DraftKey,\n    ) -> Option<&'a mut SingleDraft> {\n        match state {\n            State::Single(drafts) => drafts.get_mut(key),\n            _ => None,\n        }\n    }\n\n    /// Remove the `single_message` draft from live state at finalize/cancel so","sourceCodeStart":511,"sourceCodeEnd":547,"githubUrl":"https://github.com/zeroclaw-labs/zeroclaw/blob/88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc/crates/zeroclaw-channels/src/matrix.rs#L511-L547","documentation":"Matrix draft bookkeeping keeps a per-room State enum; insert_single may only run while the room is in State::Single (single-message progress mode, where one message is repeatedly edited in place). The let-else destructure fails on any other variant and aborts. The error means a single-message draft was recorded while the room's draft mode was partial or multi, or otherwise not Single.","triggerScenarios":"Calling insert_single(state, key, draft) when state is not State::Single - the room is still in Partial streaming mode or a multi-message lifecycle owns it, while the single-message update path registers its draft.","commonSituations":"Flipping a room between progress modes (partial to single) while a previous mode's inserts are still in flight; refactors that changed mode-selection logic; tests for single-message edit/redact/flush semantics constructing the wrong initial state.","solutions":["Transition the room to State::Single before the single-message update path inserts.","Audit mode selection: one room must not be treated as single-message while partial/multi drafts exist.","In tests, build the state in the Single variant before calling insert_single."],"exampleFix":"// before\ndrafts::insert_single(&mut state, key, draft)?;\n\n// after\nif !matches!(state, State::Single(_)) {\n    state = State::Single(Default::default());\n}\ndrafts::insert_single(&mut state, key, draft)?;","handlingStrategy":"type-guard","validationCode":null,"typeGuard":"fn in_single_state(state: &State) -> bool {\n    matches!(state, State::Single(_))\n}","tryCatchPattern":null,"preventionTips":["One room, one draft mode: enforce mode selection before the single-message update path runs.","Transition to Single and insert atomically; never insert across a mode flip.","Construct test state in the Single variant when exercising single-message edit/redact/flush paths."],"tags":["matrix","draft","state-machine","enum-variant","single-message"],"backgroundTag":"illegal-state-transition","analyzedSha":"88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc","analyzedAt":"2026-08-23T01:07:41.857Z","schemaVersion":2},"datasetVersion":"2026-08-23T08:06:27.607Z"}