Skip to content

Build1 publisher3 min readPublished

Silent WordPress bugs point at the hook that fired one step too late

A dev.to walkthrough of WordPress's five bootstrap windows yields a usable triage rule, because calling too early throws while calling too late does nothing, and load order decides which environment shows you the bug.

The Engineer · Build desk

Illustration accompanying Silent WordPress bugs point at the hook that fired one step too late

What happened

  • The mirror case, add_theme_support( 'post-thumbnails' ) called in init, is ignored with no error at all, because WordPress has already passed the point where a theme declares feature support.
  • Whether either bug appears depends on plugin load order, theme version and whether object caching is active, so the code can pass locally and fail silently in production with an empty error log.
  • The post maps five bootstrap windows in sequence and assigns each a class of call, naming after_setup_theme as the one correct place to declare theme feature support.

Compiled by The EngineerSomething wrong?How this is made

Why it matters

  • decision Triage begins with where in the sequence a call ran, because each of the five windows fails a different way when missed.
  • constraint A green local run no longer clears a hook change, since reproduction needs the production plugin set, theme version and cache state.
  • capability Gating on class_exists() at plugins_loaded lets a plugin lose a feature when WooCommerce is absent instead of taking the site down with it.
  • cost The post's case for buying in help has no numbers on either side, so a team cannot weigh it against what its own debugging hours actually cost.

The useful thing in the dev.to walkthrough is that the two failure modes are direction-coded. Call too early and you get a fatal error or behaviour that varies between installs. The post's example is register_post_type() in plugins_loaded, where taxonomies and other plugins' dependencies aren't ready yet [2]. Call too late and you get silence: add_theme_support( 'post-thumbnails' ) in init is ignored with no error, because WordPress has already passed the point where a theme declares feature support [3]. So the direction of the error tells you which side of the call site to search [18].

Both cases are, in the post's words, "correct code in the wrong hook" [4]. The symptom lands far from the cause: a missing featured image looks like a theme bug when the real problem is a hook called one step too late [7]. Translations fail the same way, with a text domain loaded after translatable strings have already been output simply not taking [5].

Reproduction is the other half. The post says whether the bug fires depends on plugin load order, theme version, or whether object caching is active, which produces the familiar "works on my local" with nothing in the production error log [6]. With dozens of plugins each hanging code off its own bootstrap hook, the post argues a load-order bug becomes nearly impossible to reproduce alone [15].

The placement rules it gives, in order:

1. muplugins_loaded, before any regular plugin or the theme, for network-wide security settings that cannot be switched off from wp-admin [9]. 2. plugins_loaded, where plugins are loaded but the theme is not, for load_plugin_textdomain() and for class_exists() dependency checks [10]. 3. after_setup_theme, which the post calls the one correct place to declare theme feature support, including add_theme_support() and register_nav_menus() [11]. 4. init, where the environment is ready and registering content is safe [12]. 5. wp_loaded, the last safe point before WordPress parses the request [13].

The dependency check is the part worth copying. The example gates on class_exists( 'WooCommerce' ) and returns when it is absent, disabling the feature instead of throwing a fatal error [14]. mu-plugins get the earliest window precisely because they have no Deactivate button [9].

Where the post is weaker is on how hard the early failure actually is. It opens by saying the site crashes with a fatal error [1]. Later it answers its own question about why register_post_type() sometimes throws and sometimes just does not work by saying it depends on how early the hook fires, and that in plugins_loaded a fatal error is possible [17]. Those are two different claims, and only the second is testable against a stock install. The post also argues that WordPress development services pay for themselves on this class of bug, on the grounds that hunting it alone usually costs far more than a specialist's consultation, without giving a figure for either side [16].

What to watch

  • A test on a stock install, with object caching on and off, would show which of the three variables the post names actually flips the symptom.
  • Whether register_post_type() at plugins_loaded fatals deterministically or only when a taxonomy dependency is registered by another plugin.
  • Whether WordPress ever emits a notice for add_theme_support() called after its window, instead of ignoring the call.
Loading claim ledger
Loading source directory links
Loading share composer
Loading topic controls
Loading related stories