astrid-runtime/astrid · error

models response too large

Error message

models response too large (exceeded {MAX_RESPONSE_BYTES} bytes; aborted mid-stream)

What it means

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.

Solutions

  1. Ensure the endpoint returns a small models-list JSON body
  2. Put a proxy in front that caps/trims the response size
  3. Fix the server to advertise and send only the compact model list
Defensive patterns

Strategy: try-catch

Try / catch

match fetch_options(opts, values).await {
    Err(e) if e.to_string().contains("too large") || e.to_string().contains("aborted mid-stream") => {
        eprintln!("response exceeded size cap; enter model manually.");
        prompt_free_text()?
    }
    other => other?,
}

Prevention

When it happens

Trigger: 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.

Common situations: Malicious or misconfigured server streaming unlimited data; proxy stripping Content-Length while returning a large payload.

Understand the failure class

Background: payload too large / request exceeds maximum size: why libraries cap bytes and how to fix oversize payloads — this error's family across 50 libraries.

Related errors


AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09). Data as JSON: /api/errors/132afced8f87d584. Report an issue: GitHub.

Appendix: source

Thrown at crates/astrid-cli/src/commands/capsule/model_discovery.rs:283

/// **before** each chunk is appended, so the buffer never exceeds the cap
/// (the transfer is aborted the moment the next chunk would cross it). This
/// is the streaming guard the up-front `Content-Length` check cannot
/// provide: a chunked / unknown-length (or lying-`Content-Length`) response
/// is bounded by the bytes actually read, not by an advisory header.
///
/// Rejects an over-cap body and any non-UTF-8 payload; both errors map to
/// the free-text fallback at the call site.
async fn read_capped_body(response: reqwest::Response) -> anyhow::Result<String> {
    use futures::StreamExt;

    let mut stream = response.bytes_stream();
    let mut body: Vec<u8> = Vec::new();
    while let Some(chunk) = stream.next().await {
        let chunk = chunk.context("error reading models response body")?;
        // Check before appending so we never hold more than the cap plus the
        // tail of one already-received chunk. The moment the total would
        // exceed the limit we abort — the rest of the stream is dropped.
        anyhow::ensure!(
            body.len().saturating_add(chunk.len()) <= MAX_RESPONSE_BYTES,
            "models response too large (exceeded {MAX_RESPONSE_BYTES} bytes; aborted mid-stream)"
        );
        body.extend_from_slice(&chunk);
    }
    String::from_utf8(body).context("models response was not valid UTF-8")
}

/// Bound an in-memory body and decode it as UTF-8 (test helper for the
/// streaming guard's cap/decoding logic).
///
/// Rejects a body larger than [`MAX_RESPONSE_BYTES`] and any non-UTF-8
/// payload — the same invariants [`read_capped_body`] enforces while
/// streaming.
#[cfg(test)]
fn decode_capped_body(body: &[u8]) -> anyhow::Result<String> {
    anyhow::ensure!(
        body.len() <= MAX_RESPONSE_BYTES,

View on GitHub (pinned to affd8760f4)