Skip to content

Retry Typst Google-font fetches, check HTTP status, keep diagnostic detail in smoke-all (bd-qvl8t3hy) - #820

Merged
cscheid merged 10 commits into
mainfrom
bugfix/bd-qvl8t3hy-typst-google-font-flake
Oct 12, 2026
Merged

cscheid merged 10 commits into
mainfrom
bugfix/bd-qvl8t3hy-typst-google-font-flake

Conversation

@cscheid

@cscheid cscheid commented Oct 12, 2026

Copy link
Copy Markdown
Member

Closes bd-qvl8t3hy. Plan: claude-notes/plans/2026-10-10-typst-google-font-flake.md.

Problem

cargo xtask verify runs smoke-all with network access, and the Typst Google-font fixtures occasionally failed with Q-21-4 ("Failed to download Google font") on a transient fetch error. Two things made this worse than a flake:

  • The native fetch_url never checked the HTTP status, so a 503 from fonts.googleapis.com came back as a successful fetch of an HTML error page, and it had no request timeout at all.
  • The test harness collapsed each DiagnosticMessage to its title, so the failure log never said which URL or which error was involved.

Changes

quarto-core / typst_google_fonts.rs — the CSS request and each font-file download go through a three-attempt schedule (250 ms, then 1 s). The sleep is injected via RetrySchedule, so unit tests run without waiting and the wasm32 build never references a blocking sleep. Q-21-4 now reports the final error and the attempt count.

quarto-system-runtime / native.rs — one shared reqwest::blocking::Client with a 30 s request timeout. A non-2xx status is now RuntimeError::Network("HTTP <status> for <url>"); transport failures use the Network variant too. The native fetch_url_hardened override enforces its policy timeout, which the trait default never did. Loopback tests cover a 200, a 503, and a stalled connection.

quarto-test — render diagnostics become harness log messages carrying the full plain-text rendering (title, problem, hints) via one shared helper for both render paths. Multi-line messages are indented under their level prefix in assertion failures. printsMessage regexes are unanchored, so existing fixtures are unaffected.

quarto-error-catalog — the Q-21-4 entry describes the retry, timeout, and error-status cases.

Behavior change for Lua filters

pandoc.mediabag.fetch (and anything else going through fetch_url) on a URL that answers 404 or 503 now returns nil with a stderr line, instead of handing the HTML error page back as content. This is intentional (design decision 2 in the plan).

Verification

  • cargo xtask verify --skip-hub-build green at 8f25d13 (14 steps, 16102 tests, including smoke_all with network).
  • WASM build via npm run build:wasm green, no new warnings.
  • No fixture edits; the five Google-font fixtures stay under the default noErrorsOrWarnings assertion.

Follow-up

bd-f48l642w tracks caching the Google Fonts CSS response so it is not refetched on every render.

🤖 Generated with Claude Code

cscheid and others added 10 commits October 10, 2026 17:08
Plan skeleton with triage verdict (ready to design). Confirms the strand's
analysis at HEAD and adds three findings: native fetch_url never checks HTTP
status, the fetch closure's String error cannot drive a retry policy, and the
font CSS is refetched on every render regardless of cache state.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Uniform 3-attempt retry in typst_google_fonts.rs; native fetch_url gets a
30 s timeout and treats non-2xx as an error for all callers; fixtures stay
under the default noErrorsOrWarnings; the harness keeps the full plain-text
diagnostic rendering. CSS caching is bd-f48l642w.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…d-qvl8t3hy)

The harness turned each render diagnostic into a LogMessage carrying only
the title, so a noErrorsOrWarnings failure in CI said "Google font not
available to Typst" and nothing about which URL or which error. Render the
diagnostic as plain text (title, problem, details, hints; no ANSI, no OSC 8)
instead, through one diagnostic_to_message helper shared by the project and
single-document paths. printsMessage regexes are unanchored, so existing
matchers are unaffected. Multi-line messages are indented under their
[LEVEL] prefix in assertion failure output.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…rl (bd-qvl8t3hy)

fetch_url was a bare reqwest::blocking::get: no timeout, so a stalled
connection could hang a render or a CI run, and no status check, so a 404
or 503 came back as Ok with the HTML error page as the body. The Typst
Google-font path then parsed that page as a stylesheet and told the user
to check the family's spelling.

One shared blocking client now carries a 30 s request timeout (matching
quarto-source-fetch), a non-2xx status is an error that names the code
and the URL, and failures use RuntimeError::Network rather than
NotSupported. The native fetch_url_hardened override enforces its
FetchPolicy timeout, which the trait default never did on native.

Loopback TcpListener tests cover the 200, 503, and stalled-connection
cases without a network.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…3hy)

stage_google_font gave up on a family after a single failed request, so
one transient network error on a CI runner produced a Q-21-4 and failed
the smoke-all noErrorsOrWarnings check for the whole render. Both the CSS
request and each file download now go through fetch_with_retry under a
RetrySchedule: three attempts, waiting 250 ms and then 1 s between them.
The schedule's sleep is injected (the module also compiles for wasm32, and
tests run it without waiting); the native caller passes
RetrySchedule::blocking(). The Q-21-4 text reports the final error and the
attempt count.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…cases (bd-qvl8t3hy)

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@posit-snyk-bot

posit-snyk-bot commented Oct 12, 2026 •

Copy link
Copy Markdown
Contributor

✅ Snyk checks have passed. No issues have been found so far.

Status Scan Engine Critical High Medium Low Total (0)
✅ Open Source Security 0 0 0 0 0 issues
✅ Licenses 0 0 0 0 0 issues

💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse.

@cscheid
cscheid merged commit e0278e9 into main Oct 12, 2026
11 of 13 checks passed
@cscheid
cscheid deleted the bugfix/bd-qvl8t3hy-typst-google-font-flake branch October 12, 2026 18:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants