Skip to main content

max / makenotwork

Answer a screen on the seam from a blocking thread `served_document_mount` has never worked for a screen that reads the database, and /fan-plus has been panicking for every signed-in reader since `26518237` put it on the seam. The mount's handler is an ordinary axum async fn, so it runs on a runtime worker. `answer` reaches the database through `Viewer::block_on`, and `block_on` from a runtime worker panics: "Cannot start a runtime from within a runtime". Every other described screen is dispatched by `quasi_axum` on `spawn_blocking` for exactly this reason -- the router is sync by quasi's decision 6 -- and this mount is the one dispatch in the tree that is ours rather than the adapter's, so it is the one place that had to say so and did not. Why it was not caught: the four screens on the seam before this session are /policy, /team, /use-cases and /fan-plus, and the first three read nothing. /fan-plus reads a membership only when somebody is signed in, and its module header says so ("a visitor's request makes no query at all"), so `fan_plus_page_renders_for_anonymous` passed while three of its siblings failed. The residual tests could not see it either: they build markup, and this is about which thread builds it. Found running the integration suite against the nine screens this session added, seven of which read a database on every request.
Co-Authored-By
Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session
https://claude.ai/code/session_01P8ostB2UmZJGj5WjSHRSot
Author: Max Johnson <me@maxj.phd> · 2026-09-08 03:24 UTC
Commit: 96befbbb2fd67426cfa20ce3860786b25880c55a
Parent: 5ef75a3
1 file changed, +33 insertions, -2 deletions
@@ -913,7 +913,16 @@
913 913 };
914 914
915 915 let carried = Carried::of(&mut parts).await;
916 - match answer(&viewer, &carried) {
916 +
917 + // On a blocking thread. See `served` for why.
918 + let answered = match tokio::task::spawn_blocking(move || answer(&viewer, &carried))
919 + .await
920 + {
921 + Ok(answered) => answered,
922 + Err(_) => return axum::http::StatusCode::INTERNAL_SERVER_ERROR.into_response(),
923 + };
924 +
925 + match answered {
917 926 Ok(markup) => (
918 927 [(quasi_axum::htmx::RETARGET, format!("#{region}"))],
919 928 axum::response::Html(markup),
@@ -1068,7 +1077,29 @@
1068 1077 };
1069 1078
1070 1079 let carried = Carried::of(&mut parts).await;
1071 - match answer(&viewer, &carried) {
1080 +
1081 + // On a blocking thread, for the reason `quasi_axum` dispatches
1082 + // every other screen on one: `answer` reaches the database
1083 + // through `Viewer::block_on`, and `block_on` from a runtime
1084 + // worker panics with "Cannot start a runtime from within a
1085 + // runtime". This mount is the one dispatch in the tree that is
1086 + // ours rather than the adapter's, so it is the one place that
1087 + // has to say so.
1088 + let held = (viewer, carried);
1089 + let (answered, held) = match tokio::task::spawn_blocking(move || {
1090 + let outcome = answer(&held.0, &held.1);
1091 + (outcome, held)
1092 + })
1093 + .await
1094 + {
1095 + Ok(answered) => answered,
1096 + // A panic in the answer. Reported as ours, because it is,
1097 + // and the same way the adapter reports one.
1098 + Err(_) => return axum::http::StatusCode::INTERNAL_SERVER_ERROR.into_response(),
1099 + };
1100 + let viewer = held.0;
1101 +
1102 + match answered {
1072 1103 Ok(Served { screen, markup }) => {
1073 1104 axum::response::Html(renderer(&viewer).served(&screen, &markup))
1074 1105 .into_response()