Opened 10 hours ago
#66429 new defect (bug)
Fonts: theme.json font loading failures are silent even with WP_DEBUG enabled
| Reported by: | manucastle78 | Owned by: | |
|---|---|---|---|
| Priority: | normal | Milestone: | Awaiting Review |
| Component: | Themes | Version: | trunk |
| Severity: | normal | Keywords: | dev-feedback |
| Cc: | Focuses: |
Description
When a font declared in theme.json fails to load or is ignored, WordPress
produces no diagnostic output. In my environment WP_DEBUG, WP_DEBUG_LOG, and
WP_DEBUG_DISPLAY are all enabled and debug.log is wp-content/debug.log, yet
I see:
- no entry in
debug.log - no PHP notice or warning on screen
- no Site Health warning
- no message in the Site Editor's Fonts screen
The only evidence is a 404 in the web server access log and the browser falling
back to another font. This makes font problems in theme.json effectively
undebuggable.
I originally hit this while adding a Montserrat family to theme.json. Montserrat
was already installed in the Font Library with the same slug (montserrat), so the
theme's fontFace definitions were silently dropped. No warning was produced even
with WP_DEBUG enabled. I have also reproduced the same silence with a
fontFace.src pointing to a file that does not exist.
### Why it is silent
WP_Font_Face_Resolver::to_theme_file_uri()(wp-includes/fonts/class-wp-font-face-resolver.php) convertsfile:./...to a theme URL viaget_theme_file_uri(), but never checksfile_exists(). A missing font file only becomes a browser 404.
WP_Font_Face::validate_font_face_declarations()(wp-includes/fonts/class-wp-font-face.php) reports invalid faces with_doing_it_wrong(), which only emits a message whenWP_DEBUGis true, then skips the face. There is noerror_log()call and no admin notice.
- When a theme font family has the same slug as a Font Library family, the custom
family wins during merge. The theme's
fontFacearray is dropped before it is validated or printed, and nothing is logged.
WP_Theme_JSON_Resolver::read_json_file()(wp-includes/class-wp-theme-json-resolver.php) returns an empty array when decoding fails.wp_json_file_decode()reports the error throughwp_trigger_error(), which returns early whenWP_DEBUGis false. On a production site, an invalidtheme.jsonis silently ignored.
### Steps to reproduce
Option A — missing file
- WordPress 7.1.3, block theme,
WP_DEBUG=true,WP_DEBUG_LOG=true,WP_DEBUG_DISPLAY=true. - Add this to
theme.json:
{
"settings": {
"typography": {
"fontFamilies": [
{
"fontFamily": "Montserrat, sans-serif",
"name": "Montserrat",
"slug": "montserrat-repro",
"fontFace": [
{
"fontFamily": "Montserrat",
"fontWeight": "400",
"fontStyle": "normal",
"src": ["file:./assets/fonts/missing.woff2"]
}
]
}
]
}
}
- Load the frontend.
- Check wp-content/debug.log and the page output.
Option B — slug shadowing
- Install Montserrat in the Font Library (slug montserrat).
- Add a montserrat font family with fontFace entries to theme.json.
- Load the Site Editor's Fonts screen and the frontend.
- Check wp-content/debug.log.
Expected
A diagnostic identifying the missing file or ignored family. At minimum, an entry
in debug.log when WP_DEBUG and WP_DEBUG_LOG are enabled, plus a Site Health or
Fonts screen notice when a theme font family is shadowed by a Font Library family.
Actual
No debug.log entry and no displayed error. The font silently falls back.
Suggested fix
- Check file_exists() in to_theme_file_uri() (or before generating the
@font-face) and trigger a warning naming the missing file and family.
- Replace the _doing_it_wrong() calls in WP_Font_Face with wp_trigger_error()
and add a core logger hooked to wp_trigger_error_always_run (WP 7.0+), or at
least use error_log() for invalid font faces.
- Emit a warning when a theme font family is shadowed by a Font Library family with
the same slug.
- Surface validation results in Site Health and the Fonts UI.
Environment
- WordPress 7.1.3
- PHP 8.4.x
- Block theme Frost
- MAMP / nginx, macOS
- WP_DEBUG=true, WP_DEBUG_LOG=true, WP_DEBUG_DISPLAY=true
- debug.log path: wp-content/debug.log
![(please configure the [header_logo] section in trac.ini)](/chrome/site/your_project_logo.png)