{"record":{"id":"61dbc55adfe77c0e","repo":"astrid-runtime/astrid","slug":"models-response-too-large-advertised-bytes-li","errorCode":null,"errorMessage":"models response too large (advertised {} bytes; limit {MAX_RESPONSE_BYTES})","messagePattern":"models response too large \\(advertised (.+?) bytes; limit (.+?)\\)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/astrid-cli/src/commands/capsule/model_discovery.rs","lineNumber":245,"sourceCode":"    let mut request = client.get(&url);\n    if let Some(token) = bearer {\n        request = request.bearer_auth(token);\n    }\n\n    let response = request.send().await?;\n    anyhow::ensure!(\n        response.status().is_success(),\n        \"models endpoint returned HTTP {}\",\n        response.status()\n    );\n\n    // Cap the body: the endpoint is operator-supplied and otherwise\n    // unbounded. Reject up-front on an advertised over-limit length so a\n    // hostile `Content-Length` can't even start a large transfer (fast\n    // path), then stream-read with the same bound so an absent/lying length\n    // (e.g. a chunked response with no `Content-Length`) cannot OOM the\n    // installer either. An over-limit response errors → free-text fallback.\n    anyhow::ensure!(\n        response\n            .content_length()\n            .is_none_or(|len| len <= MAX_RESPONSE_BYTES as u64),\n        \"models response too large (advertised {} bytes; limit {MAX_RESPONSE_BYTES})\",\n        response.content_length().unwrap_or_default()\n    );\n    let body = read_capped_body(response).await?;\n    let options = parse_options_response(&body, opts.select_or_default());\n    anyhow::ensure!(\n        !options.is_empty(),\n        \"models endpoint returned no usable options\"\n    );\n    Ok(options)\n}\n\n/// Stream the response body into memory under a hard [`MAX_RESPONSE_BYTES`]\n/// cap, then decode it as UTF-8.\n///","sourceCodeStart":227,"sourceCodeEnd":263,"githubUrl":"https://github.com/astrid-runtime/astrid/blob/affd8760f44190dbdfbec23403f4c4b642c33112/crates/astrid-cli/src/commands/capsule/model_discovery.rs#L227-L263","documentation":"To guard against hostile operator-supplied endpoints, fetch_options rejects up-front any response whose advertised Content-Length exceeds MAX_RESPONSE_BYTES, before reading the body. This bounds memory use from untrusted servers.","triggerScenarios":"The models endpoint responds with a Content-Length header larger than MAX_RESPONSE_BYTES — e.g. pointing at a huge file, a misconfigured proxy, or a malicious server.","commonSituations":"Pointing discovery at a generic file host or wrong service that returns large payloads; server misbehaving with giant advertised bodies.","solutions":["Point the endpoint at a real models-listing API that returns small JSON responses","If the server genuinely returns more than the cap, reduce response size or proxy it through a filtering service","Serve the list from a trimmed endpoint"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"if let Some(len) = head.content_length() {\n    if len > MAX_RESPONSE_BYTES {\n        eprintln!(\"response too large; skipping discovery\");\n        return Ok(());\n    }\n}","typeGuard":null,"tryCatchPattern":"match fetch_options(opts, values).await {\n    Err(e) if e.to_string().contains(\"too large\") => prompt_free_text()?,\n    other => other?,\n}","preventionTips":["Point capsule endpoints only at models-list APIs, not arbitrary file hosts","Keep model listings compact; paginate or trim server responses","Treat discovery errors as non-fatal and rely on the free-text fallback"],"tags":["http","size-limit","security"],"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"}