A client site I was rebuilding on a block theme (FSE) had a strange bug on its contact page. I had just added two new patterns, contact-hero and contact-layout, as files inside the theme's patterns/ folder. Open the editor, both should appear in the pattern inserter. They did not. The only patterns listed were the two older ones from the day before, coverage-regions and shield-ready. The two new ones were simply gone. No error, no warning, nothing.
What made it maddening: the files were clearly on disk. The header comments were valid. The slugs were unique. If I had mistyped something, I would at least get a notice or a broken pattern. This was different: WordPress behaved as if the files had never existed. The four-pattern contact page rendered with only two, the new slots silently empty. Nothing in error_log, nothing anywhere.
Dead ends
My first instinct was that the pattern files themselves were wrong. I diffed the headers of the two new files against the two working ones. The Title:, Slug:, and Categories: lines matched exactly. I renamed the slugs. I stripped the markup down to a single bare <!-- wp:paragraph --> to rule out a block that failed to parse. Still nothing.
Then I suspected ordinary caching. Purged object cache, restarted PHP, hard-refreshed the editor. Nothing. I even wondered about PHP opcache holding a stale copy, so I ran opcache_reset(). Still two patterns.
The thing that finally redirected the investigation: the old patterns showed, the new ones did not. If pattern registration were fully broken, both would vanish. But this was selective: the old ones present, the new ones ignored. That is not a parsing bug. That is the shape of a cache whose contents froze the moment those two old patterns were first registered.
Root cause
In a block theme, WordPress auto-registers patterns from the patterns/ folder. What few people realize: that folder scan is not repeated on every request. WordPress caches the entire pattern list in a site-transient named wp_theme_files_patterns-{md5(stylesheet_dir)}, with a one week lifetime.
The transient holds not just the pattern list but a version field recording the theme version when the cache was written. When WordPress goes to read it, it compares that stored version against the Version in style.css. If they match, it uses the cache as-is and never bothers to rescan the patterns/ folder. Which means: as long as the theme version does not change, adding a new patterns/*.php file goes completely undetected until the transient expires on its own, a week later.
That was exactly it. The two old patterns went into the cache when the transient was first written. I added two new files without touching the version number, so WordPress kept serving the stale snapshot with only two patterns. Not the files, not the editor. The pattern list was cached, and I had no signal because a transient surfaces in no UI.
The fix
Two ways, both correct.
The quick manual one: bump Version in style.css every time you add a pattern. The version change makes the cached version mismatch, so WordPress discards the cache and rescans.
/*
Theme Name: Client Site
Version: 1.4.2
*/But relying on remembering to bump the version on every new pattern is fragile. The fix I made permanent: a small guard in inc/patterns.php, hooked to init at priority 1 (before WordPress reads the pattern cache), that compares each pattern file's mtime against the time the cache was written. If any file is newer than the cache, delete the transient.
add_action( 'init', function () {
$dir = get_stylesheet_directory();
$key = 'wp_theme_files_patterns-' . md5( $dir );
// A site transient stores its expiry timestamp in an option.
$timeout = (int) get_site_option( '_site_transient_timeout_' . $key );
if ( ! $timeout ) {
return; // no cache yet, let WordPress build it.
}
$written_at = $timeout - WEEK_IN_SECONDS;
// Any pattern file newer than the cache?
$newest = 0;
foreach ( glob( $dir . '/patterns/*.php' ) as $file ) {
$newest = max( $newest, filemtime( $file ) );
}
if ( $newest > $written_at ) {
delete_site_transient( $key );
}
}, 1 );Because the site transient lives one week, its write time is the stored timeout minus WEEK_IN_SECONDS. The moment a pattern file has a newer mtime, the transient is deleted and WordPress rescans the patterns/ folder on the same request. New patterns appear immediately, with nothing for me to remember.
For a development machine there is a cleaner third path: turn on WordPress development mode. With define( 'WP_DEVELOPMENT_MODE', 'all' ); (or 'theme') in wp-config.php, WordPress skips the pattern cache entirely, so every file change shows up right away. I use that locally and keep the mtime guard for production.
Takeaways
- If new block theme patterns do not show but old ones do, suspect the pattern list cache, not the files.
- The pattern list is cached in the site-transient
wp_theme_files_patterns-{md5(stylesheet_dir)}for one week. - WordPress only invalidates this cache when
Versioninstyle.csschanges, not when you add apatterns/*.phpfile. - Fastest manual fix: bump the theme
Versioneach time you add a pattern. - Permanent fix: a guard on
initpriority 1 that compares each pattern file'sfilemtime()against the cache write time, thendelete_site_transient()if any is newer. - Locally, set
WP_DEVELOPMENT_MODEtothemeorallso the pattern cache is skipped entirely.
