Summary
When prerender_diagrams: true is set and the configured Kroki backend (default https://kroki.io/) is slow or unreachable on the /mermaid/svg POST endpoint, tinkerdown serve hangs in Discover() indefinitely. The YAML config comment for prerender_diagrams advertises "graceful degradation … falls back to client-side runtime for any block Kroki refuses" but the failure mode is currently a startup deadlock, not a fallback.
Repro
- With
prerender_diagrams: true (the default in repos like livetemplate/docs) and a content tree containing at least one \``mermaid` block.
tinkerdown serve content/
- While
kroki.io/mermaid/svg is unresponsive (e.g. transient infra issue, DNS, throttle), the process logs through [Routes] loaded N proxy route(s) and stops there. The "🌐 Server running at …" line never appears; HTTP listener never binds.
curl --max-time 5 -X POST -H "Content-Type: text/plain" --data 'graph TD\nA-->B' https://kroki.io/mermaid/svg returning a timeout reproduces the upstream condition. Tinkerdown's HTTP client used in internal/diagrams/kroki.go:KrokiRenderer.fetch doesn't have a per-request timeout, so the synchronous Discover() call to processMermaid() blocks waiting on http2ClientConn.roundTrip.
Expected
- Either: per-request timeout on the Kroki client (e.g. 5s, configurable) and on timeout/error, fall back to client-side rendering for that specific block (the documented "graceful degradation").
- Or: render diagrams asynchronously after the server is bound, so a slow/failing upstream doesn't gate startup.
Actual
Discover() blocks in processMermaid → KrokiRenderer.Render → fetch → http2ClientConn.roundTrip. Server never starts; nothing on port; SIGQUIT goroutine dump shows goroutine 1 still waiting on the Kroki HTTP roundtrip.
Encountered while
Folding the todos recipe into livetemplate/docs. The kroki.io endpoint was timing out; tinkerdown wouldn't start locally. Worked around by setting prerender_diagrams: false in livetemplate/docs#13, which ships the 3.3MB mermaid client runtime to readers on the 5 affected pages instead of pre-rendering server-side.
Suggested fix shape
In internal/diagrams/kroki.go:fetch, build the http.Client with Timeout: 10 * time.Second (or surface as a config knob). On non-2xx response or net error, return an error that processMermaid interprets as "leave the block as a \``mermaid` code fence" so the client-side runtime path picks it up — same as the existing per-block-error path the comment refers to.
Tested locally that flipping the flag off recovers fully; the 5 mermaid pages render client-side cleanly with no observable regression beyond the bundle weight.
Summary
When
prerender_diagrams: trueis set and the configured Kroki backend (defaulthttps://kroki.io/) is slow or unreachable on the/mermaid/svgPOST endpoint,tinkerdown servehangs inDiscover()indefinitely. The YAML config comment forprerender_diagramsadvertises "graceful degradation … falls back to client-side runtime for any block Kroki refuses" but the failure mode is currently a startup deadlock, not a fallback.Repro
prerender_diagrams: true(the default in repos likelivetemplate/docs) and a content tree containing at least one\``mermaid` block.tinkerdown serve content/kroki.io/mermaid/svgis unresponsive (e.g. transient infra issue, DNS, throttle), the process logs through[Routes] loaded N proxy route(s)and stops there. The "🌐 Server running at …" line never appears; HTTP listener never binds.curl --max-time 5 -X POST -H "Content-Type: text/plain" --data 'graph TD\nA-->B' https://kroki.io/mermaid/svgreturning a timeout reproduces the upstream condition. Tinkerdown's HTTP client used ininternal/diagrams/kroki.go:KrokiRenderer.fetchdoesn't have a per-request timeout, so the synchronous Discover() call toprocessMermaid()blocks waiting onhttp2ClientConn.roundTrip.Expected
Actual
Discover() blocks in
processMermaid → KrokiRenderer.Render → fetch → http2ClientConn.roundTrip. Server never starts; nothing on port; SIGQUIT goroutine dump shows goroutine 1 still waiting on the Kroki HTTP roundtrip.Encountered while
Folding the
todosrecipe into livetemplate/docs. The kroki.io endpoint was timing out; tinkerdown wouldn't start locally. Worked around by settingprerender_diagrams: falseinlivetemplate/docs#13, which ships the 3.3MB mermaid client runtime to readers on the 5 affected pages instead of pre-rendering server-side.Suggested fix shape
In
internal/diagrams/kroki.go:fetch, build thehttp.ClientwithTimeout: 10 * time.Second(or surface as a config knob). On non-2xx response or net error, return an error thatprocessMermaidinterprets as "leave the block as a\``mermaid` code fence" so the client-side runtime path picks it up — same as the existing per-block-error path the comment refers to.Tested locally that flipping the flag off recovers fully; the 5 mermaid pages render client-side cleanly with no observable regression beyond the bundle weight.