{"record":{"id":"06b863eb072d0b04","repo":"zeroclaw-labs/zeroclaw","slug":"matrix-marker-url-url-exceeded-max-marker-byte","errorCode":null,"errorMessage":"matrix: marker URL {url} exceeded {MAX_MARKER_BYTES}-byte cap; refusing","messagePattern":"matrix: marker URL (.+?) exceeded (.+?)-byte cap; refusing","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/zeroclaw-channels/src/matrix.rs","lineNumber":3313,"sourceCode":"    }\n\n    pub(super) async fn fetch_http(url: reqwest::Url) -> Result<Vec<u8>> {\n        let client = marker_http_client();\n        let resp = client\n            .get(url.clone())\n            .send()\n            .await\n            .with_context(|| format!(\"fetch marker URL {url}\"))?;\n        let status = resp.status();\n        if !status.is_success() {\n            bail!(\"matrix: marker URL {url} returned HTTP status {status}\");\n        }\n        let mut stream = resp.bytes_stream();\n        let mut buf = Vec::new();\n        while let Some(chunk) = stream.next().await {\n            let chunk = chunk.with_context(|| format!(\"stream chunk from {url}\"))?;\n            if buf.len().saturating_add(chunk.len()) > MAX_MARKER_BYTES {\n                bail!(\"matrix: marker URL {url} exceeded {MAX_MARKER_BYTES}-byte cap; refusing\");\n            }\n            buf.extend_from_slice(&chunk);\n        }\n        Ok(buf)\n    }\n\n    pub(super) fn thread_anchor_from_message(\n        outbox: &Outbox<'_>,\n        message: &SendMessage,\n    ) -> Option<OwnedEventId> {\n        if outbox.reply_in_thread {\n            message\n                .thread_ts\n                .as_deref()\n                .filter(|s| !s.is_empty())\n                .and_then(|s| s.parse().ok())\n        } else {\n            None","sourceCodeStart":3295,"sourceCodeEnd":3331,"githubUrl":"https://github.com/zeroclaw-labs/zeroclaw/blob/88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc/crates/zeroclaw-channels/src/matrix.rs#L3295-L3331","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"const MAX_MARKER_BYTES: u64 = 8 * 1024 * 1024;\n\nasync fn marker_size_ok(url: &str) -> bool {\n    match reqwest::Client::new().head(url).send().await {\n        Ok(resp) => resp\n            .headers()\n            .get(reqwest::header::CONTENT_LENGTH)\n            .and_then(|v| v.to_str().ok())\n            .and_then(|v| v.parse::<u64>().ok())\n            .map_or(true, |len| len <= MAX_MARKER_BYTES),\n        Err(_) => false,\n    }\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["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."],"tags":["matrix","marker","size-limit","attachment","fetch"],"backgroundTag":"response-size-limit-exceeded","analyzedSha":"88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc","analyzedAt":"2026-08-23T01:07:41.857Z","schemaVersion":2},"datasetVersion":"2026-08-23T08:06:27.607Z"}