zeroclaw-labs/zeroclaw · error · anyhow::Error
matrix: configured message_max_bytes cannot contain a thread
Error message
matrix: configured message_max_bytes cannot contain a threaded reply event
What it means
send_threaded_reply enforces the configured message_max_bytes on the fully serialized Matrix event, shrinking the reply body via next_prefix_after_oversize in a loop until the event fits. When the shrinker can no longer cut anything (next.len() == candidate.len()) and the event still exceeds the cap, the budget is below the floor of any valid threaded-reply event - the JSON envelope plus reply/thread relation metadata alone costs more than message_max_bytes - so it aborts rather than emit an oversized or structurally invalid event.
Source
Thrown at crates/zeroclaw-channels/src/matrix.rs:3618
ERROR,
::zeroclaw_log::Event::new(module_path!(), ::zeroclaw_log::Action::Fail)
.with_outcome(::zeroclaw_log::EventOutcome::Failure)
.with_attrs(::serde_json::json!({"error": format!("{}", e)})),
"make_reply_event failed"
);
anyhow::Error::msg(format!("make_reply_event failed: {e}"))
})?;
let Some(max_bytes) = message_max_bytes else {
break event;
};
let actual =
serde_json::to_vec(&event).map_or(usize::MAX, |serialized| serialized.len());
if actual <= max_bytes {
break event;
}
let next = next_prefix_after_oversize(&candidate, actual, max_bytes);
if next.len() == candidate.len() {
anyhow::bail!(
"matrix: configured message_max_bytes cannot contain a threaded reply event"
);
}
candidate = next.to_string();
};
ctx_mod::mark_seen(threads_seen, anchor).await;
let resp = room.send(reply_event).await?;
Ok(resp.response.event_id)
}
pub(super) async fn edit(
client: &Client,
room_id: &str,
event_id: &OwnedEventId,
text: &str,
message_max_bytes: Option<usize>,
) -> Result<String> {
let room = clientView on GitHub (pinned to 88bb9c8533)
Solutions
- Raise channels.matrix.message_max_bytes - the threaded event carries the serialized envelope plus reply/thread relation fields, so the budget must exceed that floor by the intended body size; increase in steps until the send succeeds.
- If the byte budget is a hard requirement, disable threaded replies for this room or channel so plain events (smaller envelope) are sent.
- Shorten fixed serialized contributors such as the quoted parent text where possible.
Example fix
# before [channels.matrix] message_max_bytes = 512 # after [channels.matrix] message_max_bytes = 4096
Defensive patterns
Strategy: validation
Validate before calling
// Startup probe: measure the serialized floor of a minimal threaded reply
// and reject budgets below it before any message is attempted.
let probe = room
.make_reply_event(
RoomMessageEventContentWithoutRelation::new(MessageType::Text(
TextMessageEventContent::plain(""),
)),
Reply {
event_id: anchor.clone(),
enforce_thread: EnforceThread::Threaded(ReplyWithinThread::No),
add_mentions: AddMentions::No,
},
)
.await?;
let floor = serde_json::to_vec(&probe)?.len();
if let Some(max) = config.message_max_bytes {
anyhow::ensure!(max > floor, "message_max_bytes {max} is below the threaded-event floor {floor}");
} Prevention
- Budget for event framing (envelope plus reply/thread relation fields) on top of visible body text when setting message_max_bytes.
- Test threaded sends under the configured budget in CI.
- Raise the budget before enabling threading on rooms with long parent messages, which enlarge reply fallbacks.
When it happens
Trigger: reply_in_thread active with a message_max_bytes so small that even the shortest candidate body cannot make the serialized threaded reply event fit: the shrink loop exhausts on the first (or an early) iteration.
Common situations: Tightening message_max_bytes to suppress long bot output and accidentally crossing below the threaded-event framing floor; reply fallbacks (the quoted parent message) inflating every reply event; SDK/server versions with larger event envelopes than the budget assumed.
Related errors
- matrix: selected single-message progress edit exceeds config
- matrix: {reason} Cannot auto-recover because channels.matrix
- matrix login requires either access_token or user_id+passwor
- matrix: configured channels.matrix.user-id ({configured}) do
- matrix: configured channels.matrix.device-id ({configured})
AI-assisted analysis of zeroclaw-labs/zeroclaw@88bb9c8533 (2026-08-23).
Data as JSON: /api/errors/79e37329af8976e0.
Report an issue: GitHub.