# Troubleshooting

URL: /docs/plugins/payload-fonts/troubleshooting

Fix the common payload-fonts failures fast — blank admin fields, fonts that never shrink, changes that don't show, and the generated files.

Symptom-first. Find your failure in bold, read the cause, apply the fix.

**Admin font field or component is blank ("not found in import map").**
The plugin registers new admin components by string path, and the import map is stale.
Run `pnpm payload generate:importmap`, then restart the dev server.

**Upload is rejected with `Invalid file type: 'application/octet-stream'`, or the file picker greys out every font.**
Windows registers no content type for `.ttf`, `.otf`, `.woff`, or `.woff2`, so the browser reports
`application/octet-stream` and the upload gate — which checks the browser-reported type — turns the
file away. The same font uploads fine from macOS, which supplies the type via UTI.
Update: `fontOriginal`'s whitelist now includes `application/octet-stream` and the file extensions,
so the upload lands and the picker stops greying fonts out. On an older version, add the same
strings via `collections.fontOriginal.overrides.upload.mimeTypes`.

**Uploaded a font but nothing renders on the page.**
Uploading a typeface only adds it to the library — it isn't selected yet.
Open the `fontSet` global, pick a **Font** for the family you want, and save. See [The `fontSet` global](/docs/plugins/payload-fonts/collections#the-fontset-global).

**Fonts save but never shrink; a typeface's Served Files count stays 0.**
Without `serverExternalPackages: ['subset-font', 'harfbuzzjs', 'fontkit']` in next.config, Next bundles the subsetter, the wasm breaks, and optimization silently skips.
Add the three packages to `serverExternalPackages` (a Quickstart step). You'll hear about it: a dev-boot probe runs a real subset and logs a loud `[payload-fonts]` error with the fix when the wasm can't load. In production the same error logs on the first font save. A typeface whose **Served Files** count stays 0 in the admin is the same signal.

**A save (or seed) warns `could not read original … that weight will NOT be served`.**
The subsetter couldn't fetch the original's bytes back from storage. On CDN-backed storage
(Vercel Blob), a freshly uploaded file can take up to \~60 seconds to become readable, so the
plugin retries fresh originals (created in the last two minutes) for over a minute — with
cache-busted reads, since a CDN can briefly go on serving a cached 404 — before giving up;
local-disk collections fail fast, because there a miss means the file is genuinely gone.
If the warning still fires, the file really is missing or unreadable: re-save the typeface
(the originals are archived, so a transient miss re-derives cleanly), or re-upload the original.

**Re-subsetted the same typeface but dev still shows the old font.**
The browser cached the `/api/fontOptimized/file/…` font file.
Hard-refresh.

**The `families` you passed dropped sans / serif / mono / display.**
`options.families` is the *complete* list — it replaces the four defaults wholesale, so `options: { families: [{ key: 'brand' }] }` drops them all.
Spread `DEFAULT_FONT_FAMILIES` to keep them and add yours:

```ts
import { fontsPlugin, DEFAULT_FONT_FAMILIES } from '@pro-laico/payload-fonts'

fontsPlugin({ options: { families: [...DEFAULT_FONT_FAMILIES, { key: 'brand', fallback: 'Georgia, serif' }] } })
```

**Reshaping `families` broke existing data.**
Dropping a family removes its `fontSet` slot (the stored selection silently vanishes) and leaves `font` docs whose `family` value is no longer an option — they fail validation on their next save.
Migrate those docs (or keep the old key) when you reshape the list.

**`definition.ts` and `public/fonts/` keep showing up as git changes.**
Both are generated — `payload-fonts-download` owns them; there's no file to hand-create or hand-edit.
Gitignore `src/app/definition.ts` and `public/fonts/`.

**`payload fonts:download` dies with `ENOENT node:crypto` (or another `node:` builtin).**
Not this plugin — it's every custom `payload <command>` on Payload **≤ 3.85.1** with Node 23.5+. Payload wraps its CLI in `tsx`, whose namespaced importer leaked a `?namespace=…` query onto `node:` builtins under Node's newer synchronous module hooks, so Node tried to open `node:crypto` as a file.
**Upgrade to `payload@>=3.85.2`**, which fixes it upstream ([payloadcms/payload#16949](https://github.com/payloadcms/payload/issues/16949)). If you're pinned to an older Payload, `payload --use-swc fonts:download` (with `@swc-node/register` installed) sidesteps tsx, or use the HTTP `payload-fonts-download` CLI, which doesn't go through Payload's bin at all.

**The build shipped with no fonts — everything falls back to your CSS stacks.**
You're on the HTTP CLI (`payload-fonts-download`), which fetches from `FONT_DOWNLOAD_URL`, so that Payload instance must be up and reachable while `prebuild` runs. When it can't reach it, the CLI resets `definition.ts` to an **empty** module and exits 0 so the build still compiles.
**Switch `prebuild` to `payload fonts:download`** — it reads the database through the Local API, so there's no site to be up, no URL, and no secret to match. Keep the HTTP CLI only if your build box genuinely can't reach the database; then make sure Payload is running during `prebuild` and watch the build log (a production build that ends up with no fonts now says so loudly, though it still exits 0).

> **The download CLI fails soft, and carries your secret.** Every handled failure writes the empty
> definition and exits 0 so the build proceeds — CI can't gate on "shipped without fonts". And the
> CLI sends `PAYLOAD_SECRET` as a bearer token to whatever `FONT_DOWNLOAD_URL` points at — keep that
> URL https and correct.

**A production font change didn't show.**
The build bakes the `fontSet` selection via `next/font`; there's no runtime cache to revalidate.
Publish a swap by re-running the download and rebuilding (`payload fonts:download` → `next build`), or wire a deploy hook via `globals.fontSet`. See [Revalidation & caching](/docs/plugins/payload-fonts/how-it-works#revalidation--caching).

**Some characters render in the fallback font.**
Subsetting is lossy to your `charset` — characters outside it aren't in the served WOFF2.
The originals are archived, so widen `charset` and re-save each typeface to re-derive. No re-upload.

**A save is rejected for a shared original, or for mixing variable and weights.**
One `fontOriginal` belongs to exactly one typeface (it keeps cascade cleanup safe), and a typeface uses a variable file XOR per-weight files, never both.
Give the second typeface its own upload, and pick one shape per typeface. See [`font`](/docs/plugins/payload-fonts/collections#font).
