fix: add CatchPanicLayer to capture handler panics

Without this, panics in async handlers silently drop the connection
and the edge proxy returns a generic 503. Now panics are caught,
logged, and returned as 500 with the panic message.

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
This commit is contained in:
Illia Polosukhin
2026-03-26 08:52:16 -07:00
co-authored by Claude Opus 4.6
parent 5692fcd37d
commit 55c8ca4347
2 changed files with 20 additions and 1 deletions
+1 -1
View File
@@ -94,7 +94,7 @@ termimad = "0.34"
# Channel integrations
axum = { version = "0.8", features = ["ws"] }
tower = "0.5"
tower-http = { version = "0.6", features = ["trace", "cors", "set-header"] }
tower-http = { version = "0.6", features = ["trace", "cors", "set-header", "catch-panic"] }
# Cron scheduling for routines
cron = "0.13"
+19
View File
@@ -629,6 +629,25 @@ pub async fn start_server(
.merge(projects)
.merge(protected)
.layer(DefaultBodyLimit::max(10 * 1024 * 1024)) // 10 MB max request body (image uploads)
.layer(tower_http::catch_panic::CatchPanicLayer::custom(
|panic_info: Box<dyn std::any::Any + Send + 'static>| {
let detail = if let Some(s) = panic_info.downcast_ref::<String>() {
s.clone()
} else if let Some(s) = panic_info.downcast_ref::<&str>() {
(*s).to_string()
} else {
"unknown panic".to_string()
};
tracing::error!("Handler panicked: {}", detail);
axum::http::Response::builder()
.status(axum::http::StatusCode::INTERNAL_SERVER_ERROR)
.header("content-type", "text/plain")
.body(axum::body::Body::from(format!("Internal Server Error: {detail}")))
.unwrap_or_else(|_| {
axum::http::Response::new(axum::body::Body::from("Internal Server Error"))
})
},
))
.layer(cors)
.layer(SetResponseHeaderLayer::if_not_present(
header::X_CONTENT_TYPE_OPTIONS,