From 36e04cf6278efe896d7fff178ec4a60582051090 Mon Sep 17 00:00:00 2001 From: Ralf Anton Beier Date: Sat, 25 Apr 2026 22:32:29 +0200 Subject: [PATCH] fix(serve): preserve response status through wrap_full_page middleware MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The wrap_full_page middleware (rivet-cli/src/serve/mod.rs) consumed the inner response body without preserving the status code, then rebuilt the response via .into_response() which defaults to 200. This silently turned every non-200 status (notably the 400 from variant_error_response) into a 200 with an error-looking layout — broke error-handling tests and gave callers wrong HTTP semantics. Capture response.status() before consuming the body, then re-apply via *wrapped.status_mut() = status after building the layout response. Likely cascade-fixes Playwright tests: - serve-variant.spec.ts:67 (unknown variant 400-style error page) - serve-variant.spec.ts:25 (variant selection navigation) - filter-sort.spec.ts:225 (?q= push-url) — middleware was potentially also stripping HX-Push-Url; verify post-fix. Implements: REQ-007 Verifies: REQ-007 Co-Authored-By: Claude Opus 4.7 (1M context) --- rivet-cli/src/serve/mod.rs | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/rivet-cli/src/serve/mod.rs b/rivet-cli/src/serve/mod.rs index a9ce589..1458203 100644 --- a/rivet-cli/src/serve/mod.rs +++ b/rivet-cli/src/serve/mod.rs @@ -1021,20 +1021,27 @@ async fn wrap_full_page( && !path.starts_with("/source-raw/") && !path.starts_with("/docs-asset/") { + // Capture status before consuming the body so we can re-apply it after + // wrapping; otherwise .into_response() defaults to 200 and silently + // turns explicit error statuses (e.g. 400 from variant_error_response) + // into successful responses. + let status = response.status(); let bytes = axum::body::to_bytes(response.into_body(), 16 * 1024 * 1024) .await .unwrap_or_default(); let content = String::from_utf8_lossy(&bytes); let app = state.read().await; - if is_print { - return layout::print_layout(&content, &app).into_response(); - } - if is_embed { - return layout::embed_layout(&content, &app).into_response(); - } - let active_variant = extract_variant_from_query(&query); - return layout::page_layout_with_variant(&content, &app, active_variant.as_deref()) - .into_response(); + let mut wrapped = if is_print { + layout::print_layout(&content, &app).into_response() + } else if is_embed { + layout::embed_layout(&content, &app).into_response() + } else { + let active_variant = extract_variant_from_query(&query); + layout::page_layout_with_variant(&content, &app, active_variant.as_deref()) + .into_response() + }; + *wrapped.status_mut() = status; + return wrapped; } response