zeroclaw-labs/zeroclaw · error · anyhow::Error
matrix: single-message draft state unavailable
Error message
matrix: single-message draft state unavailable
What it means
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.
Source
Thrown at crates/zeroclaw-channels/src/matrix.rs:529
pub(super) fn partial_contains(state: &State, key: &DraftKey) -> bool {
matches!(state, State::Partial(drafts) if drafts.contains_key(key))
}
#[cfg(test)]
pub(super) fn partial_len(state: &State) -> usize {
match state {
State::Partial(drafts) => drafts.len(),
_ => 0,
}
}
pub(super) fn insert_single(
state: &mut State,
key: DraftKey,
draft: SingleDraft,
) -> Result<()> {
let State::Single(drafts) = state else {
bail!("matrix: single-message draft state unavailable");
};
drafts.insert(key, draft);
Ok(())
}
/// Return the editable `single_message` draft for this room+draft id, if it
/// is still active.
pub(super) fn single_for_update<'a>(
state: &'a mut State,
key: &DraftKey,
) -> Option<&'a mut SingleDraft> {
match state {
State::Single(drafts) => drafts.get_mut(key),
_ => None,
}
}
/// Remove the `single_message` draft from live state at finalize/cancel soView on GitHub (pinned to 88bb9c8533)
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.
Example fix
// before
drafts::insert_single(&mut state, key, draft)?;
// after
if !matches!(state, State::Single(_)) {
state = State::Single(Default::default());
}
drafts::insert_single(&mut state, key, draft)?; Defensive patterns
Strategy: type-guard
Type guard
fn in_single_state(state: &State) -> bool {
matches!(state, State::Single(_))
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- matrix: partial draft state unavailable
- matrix: multi-message draft state unavailable
- matrix: draft message id is empty
- matrix: selected single-message progress edit exceeds config
- matrix: corruption recovery looped — aborting to avoid an in
AI-assisted analysis of zeroclaw-labs/zeroclaw@88bb9c8533 (2026-08-23).
Data as JSON: /api/errors/873ab6d3500cd1cc.
Report an issue: GitHub.