Skip to content

Kroki upstream timeout blocks discovery indefinitely (no graceful fallback) #275

Description

@adnaan

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

  1. With prerender_diagrams: true (the default in repos like livetemplate/docs) and a content tree containing at least one \``mermaid` block.
  2. tinkerdown serve content/
  3. 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.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    P2-mediumMedium: quality, platform coverage, CIbugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions