getzola/zola · info

Could not build HTML response

Error message

Could not build HTML response

What it means

in_memory_content serves in-memory build output by building a Response with Response::builder().body(Body::from(content.to_owned())).expect(...). Construction fails only if the content-type HeaderValue is invalid, so the panic indicates an invalid dynamically chosen content_type string.

Source

Thrown at src/cmd/serve.rs:361

    Response::from_parts(parts, Body::from(bytes))
}

fn in_memory_content(path: &RelativePathBuf, content: &str) -> Response {
    let content_type = match path.extension() {
        Some(ext) => match ext {
            "xml" => "text/xml",
            "json" => "application/json",
            "txt" => "text/plain",
            _ => "text/html",
        },
        None => "text/html",
    };
    Response::builder()
        .header(header::CONTENT_TYPE, content_type)
        .status(StatusCode::OK)
        .body(Body::from(content.to_owned()))
        .expect("Could not build HTML response")
}

fn method_not_allowed() -> Response {
    Response::builder()
        .header(header::CONTENT_TYPE, "text/plain")
        .status(StatusCode::METHOD_NOT_ALLOWED)
        .body(Body::from(METHOD_NOT_ALLOWED_TEXT))
        .expect("Could not build Method Not Allowed response")
}

fn io_error(err: std::io::Error) -> Response {
    match err.kind() {
        std::io::ErrorKind::NotFound => not_found(),
        std::io::ErrorKind::PermissionDenied => {
            Response::builder().status(StatusCode::FORBIDDEN).body(Body::empty()).unwrap()
        }
        _ => panic!("{}", err),
    }

View on GitHub (pinned to 61d3082821)

Solutions

  1. Sanitize the computed content_type before building the response (strip control chars)
  2. Fall back to "text/html" or "application/octet-stream" when HeaderValue::from_str fails
  3. Use HeaderValue::from_str(content_type).unwrap_or(HeaderValue::from_static("application/octet-stream")) in the builder

Example fix

// before
Response::builder()
    .header(header::CONTENT_TYPE, content_type)
    .status(StatusCode::OK)
    .body(Body::from(content.to_owned()))
    .expect("Could not build HTML response")
// after
let content_type = HeaderValue::from_str(content_type)
    .unwrap_or(HeaderValue::from_static("application/octet-stream"));
Response::builder()
    .header(header::CONTENT_TYPE, content_type)
    .status(StatusCode::OK)
    .body(Body::from(content.to_owned()))
    .expect("Could not build HTML response")
Defensive patterns

Strategy: validation

Validate before calling

let ct = http::HeaderValue::from_str(content_type)
    .map_err(|_| anyhow::anyhow!("invalid content type: {}", content_type))?;

Type guard

fn is_valid_header_value(v: &str) -> bool {
    http::HeaderValue::from_str(v).is_ok()
}

Prevention

When it happens

Trigger: Requesting a file during `zola serve` whose resolved content_type (derived from file extension/mime guess) is not a valid HTTP header value — e.g. a mime string containing control characters from a custom extension mapping.

Common situations: Serving files with unusual extensions after patching the MIME table; corruption in the mime-guessing dependency output.

Related errors


AI-assisted analysis of getzola/zola@61d3082821 (2026-09-03). Data as JSON: /api/errors/8ab9c69732d7a8b7. Report an issue: GitHub.