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 = client

View on GitHub (pinned to 88bb9c8533)

Solutions

  1. 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.
  2. If the byte budget is a hard requirement, disable threaded replies for this room or channel so plain events (smaller envelope) are sent.
  3. 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

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


AI-assisted analysis of zeroclaw-labs/zeroclaw@88bb9c8533 (2026-08-23). Data as JSON: /api/errors/79e37329af8976e0. Report an issue: GitHub.