From 1c67dd20d7d5df0099eb0f69a4c19e932546ff6d Mon Sep 17 00:00:00 2001 From: Thomas Faour Date: Thu, 23 Jul 2026 21:22:17 -0400 Subject: [PATCH] Auto-restart the whiteboard render sidecar if it crashes Root cause of "whiteboard stuck on old content, no errors anywhere": the Node sidecar had crashed at some point and, since it was just a bare backgrounded process with nothing supervising it, stayed dead permanently. Every refresh since then hit connection-refused, which get_or_refresh_whiteboard treats as a soft failure and falls back to the last successfully cached image -- so it looked exactly like a caching bug from the outside, silently, forever, with no error visible anywhere except a crash trace that had already scrolled out of the log buffer. Wrap it in a restart loop instead of a bare `&` so a future crash (a still-unknown third jsdom/Excalidraw edge case, most likely) is a few-second hiccup instead of a silent permanent outage. --- server/start.sh | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/server/start.sh b/server/start.sh index c4b2352..b243e09 100755 --- a/server/start.sh +++ b/server/start.sh @@ -3,8 +3,22 @@ # sidecar, see its own README) in the background, bound to 127.0.0.1 -- # reachable from this container's Python process, never from outside it. # Then execs uvicorn as the foreground/PID 1 process so it receives -# Docker's stop signal directly. The backgrounded Node process has no -# state worth flushing on shutdown -- fine for it to just die with the -# container. -node ./render-service/server.js & +# Docker's stop signal directly. +# +# Wrapped in a restart loop, not a bare `node ... &`: a bare background +# process that crashes stays dead for good, with nothing to bring it +# back -- turning any single render crash (a not-yet-found jsdom/ +# Excalidraw edge case, say) into a silent, permanent whiteboard outage +# that looks exactly like "stuck showing stale content forever" from the +# outside, since routers/common.py's get_or_refresh_whiteboard falls +# back to the last good cached image on every failed refresh rather than +# going blank. The 2s sleep just avoids a hot-crash-loop pegging a core +# if something's wrong at every single startup. +( + while true; do + node ./render-service/server.js + echo "whiteboard render sidecar exited (code $?) -- restarting in 2s" >&2 + sleep 2 + done +) & exec uvicorn app.main:app --host 0.0.0.0 --port 8420