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
- Sanitize the computed content_type before building the response (strip control chars)
- Fall back to "text/html" or "application/octet-stream" when HeaderValue::from_str fails
- 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
- Fall back to application/octet-stream for unresolvable MIME types
- Whitelist the content types the dev server may emit
- Never derive header values from raw user-controlled file extensions without sanitizing
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
- Could not build livereload.js response
- Could not build Method Not Allowed response
- Could not build error response
- reqwest client build
- result cache lock
AI-assisted analysis of getzola/zola@61d3082821 (2026-09-03).
Data as JSON: /api/errors/8ab9c69732d7a8b7.
Report an issue: GitHub.