{"record":{"id":"132afced8f87d584","repo":"astrid-runtime/astrid","slug":"models-response-too-large-exceeded-max-response","errorCode":null,"errorMessage":"models response too large (exceeded {MAX_RESPONSE_BYTES} bytes; aborted mid-stream)","messagePattern":"models response too large \\(exceeded (.+?) bytes; aborted mid-stream\\)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/astrid-cli/src/commands/capsule/model_discovery.rs","lineNumber":283,"sourceCode":"/// **before** each chunk is appended, so the buffer never exceeds the cap\n/// (the transfer is aborted the moment the next chunk would cross it). This\n/// is the streaming guard the up-front `Content-Length` check cannot\n/// provide: a chunked / unknown-length (or lying-`Content-Length`) response\n/// is bounded by the bytes actually read, not by an advisory header.\n///\n/// Rejects an over-cap body and any non-UTF-8 payload; both errors map to\n/// the free-text fallback at the call site.\nasync fn read_capped_body(response: reqwest::Response) -> anyhow::Result<String> {\n    use futures::StreamExt;\n\n    let mut stream = response.bytes_stream();\n    let mut body: Vec<u8> = Vec::new();\n    while let Some(chunk) = stream.next().await {\n        let chunk = chunk.context(\"error reading models response body\")?;\n        // Check before appending so we never hold more than the cap plus the\n        // tail of one already-received chunk. The moment the total would\n        // exceed the limit we abort — the rest of the stream is dropped.\n        anyhow::ensure!(\n            body.len().saturating_add(chunk.len()) <= MAX_RESPONSE_BYTES,\n            \"models response too large (exceeded {MAX_RESPONSE_BYTES} bytes; aborted mid-stream)\"\n        );\n        body.extend_from_slice(&chunk);\n    }\n    String::from_utf8(body).context(\"models response was not valid UTF-8\")\n}\n\n/// Bound an in-memory body and decode it as UTF-8 (test helper for the\n/// streaming guard's cap/decoding logic).\n///\n/// Rejects a body larger than [`MAX_RESPONSE_BYTES`] and any non-UTF-8\n/// payload — the same invariants [`read_capped_body`] enforces while\n/// streaming.\n#[cfg(test)]\nfn decode_capped_body(body: &[u8]) -> anyhow::Result<String> {\n    anyhow::ensure!(\n        body.len() <= MAX_RESPONSE_BYTES,","sourceCodeStart":265,"sourceCodeEnd":301,"githubUrl":"https://github.com/astrid-runtime/astrid/blob/affd8760f44190dbdfbec23403f4c4b642c33112/crates/astrid-cli/src/commands/capsule/model_discovery.rs#L265-L301","documentation":"read_capped_body streams the response body while enforcing MAX_RESPONSE_BYTES; if the accumulated size would exceed the cap mid-stream it aborts and drops the rest. This handles chunked responses with no (or lying) Content-Length that passed the advertised-size check.","triggerScenarios":"The endpoint sends more than MAX_RESPONSE_BYTES in a chunked/streamed response without an over-limit Content-Length — e.g. absent or false Content-Length header with a huge body.","commonSituations":"Malicious or misconfigured server streaming unlimited data; proxy stripping Content-Length while returning a large payload.","solutions":["Ensure the endpoint returns a small models-list JSON body","Put a proxy in front that caps/trims the response size","Fix the server to advertise and send only the compact model list"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"match fetch_options(opts, values).await {\n    Err(e) if e.to_string().contains(\"too large\") || e.to_string().contains(\"aborted mid-stream\") => {\n        eprintln!(\"response exceeded size cap; enter model manually.\");\n        prompt_free_text()?\n    }\n    other => other?,\n}","preventionTips":["Prefer endpoints with accurate Content-Length and small JSON bodies","Keep discovery responses well under MAX_RESPONSE_BYTES","Never point capsule discovery at untrusted or unbounded endpoints"],"tags":["http","size-limit","streaming"],"backgroundTag":"payload-too-large","analyzedSha":"affd8760f44190dbdfbec23403f4c4b642c33112","analyzedAt":"2026-09-09T21:28:12.402Z","contentChangedAt":"2026-09-09T21:28:12.402Z","schemaVersion":2},"datasetVersion":"2026-09-17T15:17:12.973Z"}