getzola/zola · info
Could not build livereload.js response
Error message
Could not build livereload.js response
What it means
serve_livereload_js builds an axum/hyper Response with Response::builder().body(...) and .expect panics if response construction fails. With static headers and a static body (LIVE_RELOAD) this only fails if hyper's builder invariants are violated, so in practice it is an invariant assertion.
Source
Thrown at src/cmd/serve.rs:271
Some(Err(e)) => {
log::error!("WebSocket error: {e}");
break;
}
None => break,
}
}
}
}
}
/// Serve livereload.js
async fn serve_livereload_js() -> impl IntoResponse {
Response::builder()
.header(header::CONTENT_TYPE, "text/javascript")
.header(header::ACCESS_CONTROL_ALLOW_ORIGIN, "*")
.status(StatusCode::OK)
.body(Body::from(LIVE_RELOAD))
.expect("Could not build livereload.js response")
}
/// Inserts build error message boxes into HTML responses when needed.
/// Used as axum middleware via `map_response`.
async fn error_injection_middleware(response: Response) -> Response {
use axum::body::to_bytes;
// Return response as-is if there are no error messages.
let has_error = SERVE_ERROR.lock().unwrap().get_mut().is_some();
if !has_error {
return response;
}
// Only inject errors into HTML responses or 404 responses.
// Don't interfere with WebSocket upgrades (101) or other special responses.
let is_html = response
.headers()
.get(header::CONTENT_TYPE)View on GitHub (pinned to 61d3082821)
Solutions
- Keep header values static/valid; validate any dynamic header content
- Refactor to return Result<Response, Infallible>-style handling or use a pre-built response constant
- Use Response::from parts construction that cannot fail for fully static responses
Example fix
// before
Response::builder()
.header(header::CONTENT_TYPE, "text/javascript")
.header(header::ACCESS_CONTROL_ALLOW_ORIGIN, "*")
.status(StatusCode::OK)
.body(Body::from(LIVE_RELOAD))
.expect("Could not build livereload.js response")
// after
let mut response = Response::new(Body::from(LIVE_RELOAD));
response.headers_mut().insert(header::CONTENT_TYPE, HeaderValue::from_static("text/javascript"));
response.headers_mut().insert(header::ACCESS_CONTROL_ALLOW_ORIGIN, HeaderValue::from_static("*"));
response Defensive patterns
Strategy: fallback
Validate before calling
// assert header values are valid at startup
let _ = http::HeaderValue::from_static("text/javascript");
let _ = http::HeaderValue::from_static("*"); Try / catch
match Response::builder()
.header(header::CONTENT_TYPE, "text/javascript")
.header(header::ACCESS_CONTROL_ALLOW_ORIGIN, "*")
.status(StatusCode::OK)
.body(Body::from(LIVE_RELOAD))
{
Ok(resp) => resp,
Err(e) => {
eprintln!("livereload response build failed: {}", e);
StatusCode::INTERNAL_SERVER_ERROR.into_response()
}
} Prevention
- Keep static response headers as from_static constants
- Never interpolate untrusted strings into header values
- Return 500 instead of panicking in dev-server handlers
When it happens
Trigger: Visiting /livereload.js during `zola serve` when the http::Response builder rejects the request (invalid header values or builder misuse) — effectively never with the current constants, but any refactor introducing an invalid header value triggers it.
Common situations: Patching the handler to add dynamic headers containing illegal characters (newlines, non-ASCII) then serving the dev site.
Related errors
- Could not build HTML 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/804e9460b738ddc7.
Report an issue: GitHub.