Skip to content

next/font/google: a 404 on a font file is reported as "Module not found" and never retried #97378

Description

@lucasmcht

Link to the code that reproduces this issue

https://github.com/lucasmcht-corp/next-font-404-repro

To Reproduce

git clone https://github.com/lucasmcht-corp/next-font-404-repro
cd next-font-404-repro
npm install
npm run build:mocked

The build exits 1 with:

Received response with status 404 when requesting https://fonts.gstatic.com/s/bitter/v42/this-file-does-not-exist.woff2
> Build error occurred
Error: Module not found: Can't resolve '@vercel/turbopack-next/internal/font/google/font'

The stylesheet is served through Next's own NEXT_FONT_GOOGLE_MOCKED_RESPONSES hook, so no CDN behaviour is involved. It points at a font file URL that does not exist, so the file fetch, which is not mocked, answers 404 on every machine, every time.

Current vs. Expected behavior

Current: a single failed font file download stops the build with Module not found: Can't resolve '@vercel/turbopack-next/internal/font/google/font'. The HTTP status appears only as a warning earlier in the log, easily lost in a large build. There is no retry.

Expected: the fetch is retried, and if it still fails the build error names the network failure, the URL and the status code, instead of an internal module.

Where it happens

In crates/next-core/src/next_font/google/mod.rs:

Why this matters outside the mock

The same path is reached with no mock at all. On 16.3.1 our production builds failed at random, on a different family each run (Bitter, Montserrat, Noto Serif), with .next deleted before each build, because the stylesheet returned by fonts.googleapis.com pointed at files fonts.gstatic.com had already rotated away. Checked again while writing this:

URL status
.../notoserif/v33/ga63aw1J5X9T9RW6...QLqNQw.woff2 (requested by the build) 404
.../notoserif/v33/ga6daw1J5X9T9RW6...WsNFHuQk.woff2 (returned today for the same family) 200

A likely trigger, offered as a hypothesis since I could not capture the stylesheet the failing build received: fonts.googleapis.com/css2 answers with cache-control: private, max-age=86400, stale-while-revalidate=604800 and vary: Sec-Fetch-Dest, Sec-Fetch-Mode, Sec-Fetch-Site. The fetcher sends no Sec-Fetch-* headers, so it addresses its own cache entry, one browsers never keep warm, which may be served stale for up to seven days while the files it names are rotated.

That hypothesis is only about what makes the fetch fail. The reproduction above is about what Next does when it does fail, which is deterministic and independent of it.

Suggested fix, in order of value

  1. Retry the font file fetch before giving up.
  2. Report the HTTP status and URL as the build error instead of a missing module.
  3. Optionally send Sec-Fetch-Dest: style on the stylesheet request, so the fetcher shares the cache entry browsers keep warm.

Related

Environment

next 16.3.1
node 22.23.0
Linux x64
App Router, next/font/google

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

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions