Skip to content

fix(producer): treat 4xx from Google Fonts as deterministic "not served", not as failClosed trigger#957

Merged
jrusso1020 merged 1 commit into
mainfrom
05-19-fix_deterministic_fonts_use_canonical_name_for_google_fonts_supplement
May 19, 2026
Merged

fix(producer): treat 4xx from Google Fonts as deterministic "not served", not as failClosed trigger#957
jrusso1020 merged 1 commit into
mainfrom
05-19-fix_deterministic_fonts_use_canonical_name_for_google_fonts_supplement

Conversation

@jrusso1020
Copy link
Copy Markdown
Collaborator

@jrusso1020 jrusso1020 commented May 19, 2026

Summary

After c8e8fdc added a Google Fonts supplement-fetch to the Path 1 (bundled-font) branch of buildFontFaceCss, every plan() call against a composition whose CSS named a non-Google family that Google Fonts 400s on (\"Segoe UI\", \"Arial\", \"Futura\", …) started failing on distributed renders with FONT_FETCH_FAILED. Distributed defaults failClosedFontFetch: true, and the existing code treated all non-2xx responses the same way — throw if failClosed, swallow otherwise. That conflates two very different failure modes.

Confirmed via curl against Google Fonts:

Family HTTP Notes
Inter 200 4 faces — self-aliased canonical
Helvetica Neue 200 2 faces — Google serves real Helvetica glyphs
Helvetica 200 2 faces — same
Arial 400 Not served
Segoe UI 400 Not served

Fix

Split the !res.ok branch in fetchGoogleFont (both CSS fetch and woff2 fetch):

  • 4xx: a deterministic "Google Fonts doesn't serve this family" answer. Same outcome on every retry → no risk to the byte-identical-retry contract. Return [] in both modes; render falls back to embedded faces / the composition's font-family chain (which is what it would have done pre-c8e8fdcf anyway).
  • 5xx (plus network / DNS / fetch exceptions): non-deterministic infrastructure failure. A retry might succeed and produce different pixels than the first attempt — exactly what failClosedFontFetch is meant to guard against. Keep failing closed in this mode.

3-line change in error handling; no call-site or FONT_ALIASES changes; no behavior change for any composition that wasn't previously broken.

Why this and not [fix at the call site]

Earlier revisions of this PR tried two other shapes that I rejected:

  1. Pass canonical.googleName to fetchGoogleFont (fetch Inter for Helvetica). Drifted style-7-prod's baseline by 67 frames because it silently bound Inter's 50+ Google weights to font-family: \"Helvetica\", expanding the weight palette beyond what the baseline captured.
  2. Skip supplement-fetch for cross-aliases (canonicalKey !== normalizedFamily). Still drifted style-7-prod by 60 frames — because Google Fonts actually serves real Helvetica glyphs under the \"Helvetica Neue\" and \"Helvetica\" names, and the current baseline depends on those real glyphs. Skipping the fetch fell back to embedded Inter, which is a different render.

The right fix is the error-semantics one: failClosedFontFetch should protect against non-determinism, not against "font doesn't exist on Google" (which is itself deterministic). With this fix, every existing composition keeps the exact behavior it has today — what changes is only that distributed renders no longer crash on 4xx.

Test plan

bun test packages/producer/src/services/deterministicFonts-failClosed.test.ts — 12/12 pass:

  • 2 new cases on failClosedFontFetch: true: 400 and 404 responses no longer throw, render resolves cleanly.
  • 2 new cases on failClosedFontFetch: true: 503 throws FONT_FETCH_FAILED, error includes URL + family.
  • 1 new case on failClosedFontFetch: false: 5xx swallowed as before.
  • Pre-existing "does NOT throw when bundled-font" test was broken by c8e8fdc (the supplement-fetch always fires for self-aliased bundled fonts now). Updated to use a successful empty CSS response, which is the actual invariant we want.

bun run --cwd packages/producer build clean. bunx oxlint + bunx oxfmt --check clean.

Unblocks

Distributed renders against compositions that name any non-Google family Google Fonts 400s on (Segoe UI, Arial, Courier New, Futura). Pairs with the c=3 worker-0-only SwiftShader probe fix shipped in #956 — the texture-launch validation bench was blocked on this issue (Segoe UI in the fixture's CSS); we ran validation on a Space-Mono fixture instead and confirmed the worker-0 fix works.

Followup (not in this PR)

Should FONT_ALIASES exist at all in a deterministic cloud renderer? Today font-family: \"Helvetica\" silently produces real Helvetica glyphs (via Google Fonts' undocumented alias serving) and font-family: \"Segoe UI\" silently produces embedded Roboto, with no warning to the author. That's a WYSIWYG violation worth its own proposal.

🤖 Generated with Claude Code

Copy link
Copy Markdown
Collaborator

@miguel-heygen miguel-heygen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

APPROVE — clean regression fix with good test coverage.

Verified:

  • All 18 googleName values match their Google Fonts listing names (including IBM Plex Mono, JetBrains Mono, EB Garamond, Noto Sans JP)
  • Alias routing correct: segoe ui → queries Roboto, helvetica neue → queries Inter, etc.
  • Emitted @font-face still declares originalCaseFamily for CSS selector matching — compositions aren't broken
  • googleName field is the right design: explicit mapping vs trying to derive (would fail for multi-word names)
  • 3 new tests cover the critical alias path + verify the emitted font-family name
  • Reworked pre-existing test now accurately tests bundled-font injection

Root cause clear: c8e8fdcf (v0.6.21+) passed raw CSS family name to fetchGoogleFont instead of the canonical Google Fonts name. Google returns 400 for non-Google families, failClosedFontFetch: true aborted the render.

This also explains the style-8-prod baseline diff from PR #946 — the font weight fetching now works correctly with canonical names.

@jrusso1020 jrusso1020 changed the title fix(producer): query Google Fonts under canonical name when supplementing aliased families fix(producer): skip Google Fonts supplement-fetch for cross-aliased system fonts May 19, 2026
@jrusso1020 jrusso1020 force-pushed the 05-19-fix_deterministic_fonts_use_canonical_name_for_google_fonts_supplement branch from 2cd0869 to 887029d Compare May 19, 2026 05:59
…ed", not as failClosed trigger

After c8e8fdc added a Google Fonts supplement-fetch to the Path 1
(bundled-font) branch, every `plan()` call against a composition whose
CSS named a non-Google family that Google Fonts 400s on (e.g.
`"Segoe UI"`, `"Arial"`, `"Futura"`) started failing on distributed
renders with `FONT_FETCH_FAILED`. Distributed renders default to
`failClosedFontFetch: true`, and the existing code treated *all* non-2xx
responses uniformly: throw if failClosed, swallow otherwise.

That conflates two very different failure modes:

  - **4xx** is a *deterministic* answer — Google Fonts does not serve
    this family, and won't serve it on retry either. The byte-identical-
    retry contract distributed renders rely on is unaffected; the render
    falls back to embedded faces / the composition's font-family chain
    (which is what it would have done pre-c8e8fdcf anyway). No reason
    to fail-close here.

  - **5xx** (and network / DNS / fetch exceptions) is *non-deterministic*
    infrastructure failure. A retry might succeed and produce different
    pixel output than the first attempt — exactly what
    `failClosedFontFetch` is meant to protect against. Keep failing
    closed in this mode.

Fix: split the !res.ok branch in both the CSS fetch and the woff2 fetch
inside `fetchGoogleFont` — only `>= 500` paired with failClosed throws;
4xx returns `[]` in both modes. Network/DNS exceptions in the catch
block are unchanged (still failClosed-gated).

This:
  - Unblocks distributed renders for compositions that name any
    cross-alias system font Google doesn't serve (Segoe UI, Arial, etc.).
  - **Preserves the current regression baseline** — Google Fonts
    actually *does* serve some non-canonical names (e.g. "Helvetica" and
    "Helvetica Neue" both return HTTP 200 with real @font-face rules,
    confirmed via curl), so the supplement-fetch still runs and binds
    those real faces to the composition's CSS family names exactly as
    today. style-7-prod (which uses `"Helvetica Neue", Helvetica, Arial,
    sans-serif`) continues to render against real Helvetica glyphs.
  - Leaves the FONT_ALIASES table and call-site untouched. The fix is
    in the right place — the error semantics inside fetchGoogleFont —
    not in any composition-aware logic upstream.

Tests:
  - 2 new positive cases on `failClosedFontFetch: true`:
    400 and 404 responses no longer throw, render falls back cleanly.
  - 2 new negative cases on `failClosedFontFetch: true`:
    503 throws `FONT_FETCH_FAILED`, error includes URL + family.
  - 1 new case on `failClosedFontFetch: false`: 5xx swallowed as before.
  - The pre-existing "does NOT throw when the HTML uses a pre-bundled
    font" test was broken by c8e8fdc (the supplement-fetch always fires
    for self-aliased bundled fonts now). Updated it to use a successful
    empty CSS response, which is the actual invariant we want.

12/12 tests pass.

Followup discussion: should `FONT_ALIASES` exist at all in a
deterministic cloud renderer? Today `font-family: "Helvetica"` in CSS
silently produces real Helvetica glyphs (via Google Fonts' undocumented
alias serving) and `font-family: "Segoe UI"` silently produces embedded
Roboto, with no warning to the author. That's a WYSIWYG violation worth
its own proposal — but not in scope for this fix.
@jrusso1020 jrusso1020 force-pushed the 05-19-fix_deterministic_fonts_use_canonical_name_for_google_fonts_supplement branch from 887029d to e7fd88b Compare May 19, 2026 06:31
@jrusso1020 jrusso1020 changed the title fix(producer): skip Google Fonts supplement-fetch for cross-aliased system fonts fix(producer): treat 4xx from Google Fonts as deterministic "not served", not as failClosed trigger May 19, 2026
Copy link
Copy Markdown
Collaborator

@miguel-heygen miguel-heygen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-approving at e7fd88b — same commit, re-stamping for require_last_push_approval.

@jrusso1020 jrusso1020 merged commit c336508 into main May 19, 2026
49 of 68 checks passed
@jrusso1020 jrusso1020 deleted the 05-19-fix_deterministic_fonts_use_canonical_name_for_google_fonts_supplement branch May 19, 2026 07:09
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