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

  1. Keep header values static/valid; validate any dynamic header content
  2. Refactor to return Result<Response, Infallible>-style handling or use a pre-built response constant
  3. 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

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


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