zeroclaw-labs/zeroclaw · error · anyhow::Error
matrix: marker URL {url} exceeded {MAX_MARKER_BYTES}-byte ca
Error message
matrix: marker URL {url} exceeded {MAX_MARKER_BYTES}-byte cap; refusing What it means
fetch_http streams the marker body and enforces a hard MAX_MARKER_BYTES cap of 8 MiB (8 * 1024 * 1024): once the buffered size plus the incoming chunk would cross the cap it refuses and aborts rather than buffer an unbounded remote resource. The fetch also runs under a 30-second timeout. Hitting it means the referenced marker content is larger than the channel will accept.
Source
Thrown at crates/zeroclaw-channels/src/matrix.rs:3313
}
pub(super) async fn fetch_http(url: reqwest::Url) -> Result<Vec<u8>> {
let client = marker_http_client();
let resp = client
.get(url.clone())
.send()
.await
.with_context(|| format!("fetch marker URL {url}"))?;
let status = resp.status();
if !status.is_success() {
bail!("matrix: marker URL {url} returned HTTP status {status}");
}
let mut stream = resp.bytes_stream();
let mut buf = Vec::new();
while let Some(chunk) = stream.next().await {
let chunk = chunk.with_context(|| format!("stream chunk from {url}"))?;
if buf.len().saturating_add(chunk.len()) > MAX_MARKER_BYTES {
bail!("matrix: marker URL {url} exceeded {MAX_MARKER_BYTES}-byte cap; refusing");
}
buf.extend_from_slice(&chunk);
}
Ok(buf)
}
pub(super) fn thread_anchor_from_message(
outbox: &Outbox<'_>,
message: &SendMessage,
) -> Option<OwnedEventId> {
if outbox.reply_in_thread {
message
.thread_ts
.as_deref()
.filter(|s| !s.is_empty())
.and_then(|s| s.parse().ok())
} else {
NoneView on GitHub (pinned to 88bb9c8533)
Solutions
- Shrink the marker resource below 8 MiB (compress or resize images; pick a thumbnail variant).
- If the content must stay large, attach it from the workspace directory (uploaded through Matrix media) instead of a marker URL.
- Verify the URL returns the intended asset (check Content-Type and Content-Length) and not an HTML page or archive.
- If a larger cap is genuinely required for your deployment, raise MAX_MARKER_BYTES in a fork, accepting the memory-cost tradeoff.
Defensive patterns
Strategy: validation
Validate before calling
const MAX_MARKER_BYTES: u64 = 8 * 1024 * 1024;
async fn marker_size_ok(url: &str) -> bool {
match reqwest::Client::new().head(url).send().await {
Ok(resp) => resp
.headers()
.get(reqwest::header::CONTENT_LENGTH)
.and_then(|v| v.to_str().ok())
.and_then(|v| v.parse::<u64>().ok())
.map_or(true, |len| len <= MAX_MARKER_BYTES),
Err(_) => false,
}
} Prevention
- Serve compressed thumbnails or renditions as markers; keep originals as workspace attachments.
- Check Content-Length with a HEAD probe when generating marker URLs.
- Remember the cap counts the whole streamed body - HTML error pages from misconfigured hosts count too.
- Watch marker fetch failure logs to catch oversized-asset regressions early.
When it happens
Trigger: Delivering a message whose marker URL serves a body larger than 8 MiB - large images, videos, or misconfigured endpoints that stream endless data - so the running byte total crosses MAX_MARKER_BYTES mid-stream.
Common situations: Pointing markers at full-resolution media or video files; a URL that returns a directory listing or generated archive; a CDN serving the raw asset instead of a thumbnail; content-type mistakes where a page downloads instead of the intended small asset.
Related errors
- matrix: marker URL {url} returned HTTP status {status}
- attachment Content-Length ({len} bytes) exceeds {} MB limit
- attachment exceeds {} MB limit
- inbound attachment exceeds {} MB limit
- WeCom max_file_size_bytes is zero
AI-assisted analysis of zeroclaw-labs/zeroclaw@88bb9c8533 (2026-08-23).
Data as JSON: /api/errors/06b863eb072d0b04.
Report an issue: GitHub.