This lesson is a single, real, closed GitHub issue, worked through in detail, because it’s a genuinely useful lesson in how to debug: the reported symptom, the reporter’s own hypothesis about what was wrong, and the actual root cause the community found were three different things. If you take one habit from this lesson, take this one: when an exotic explanation and a boring one both fit the symptoms, check the boring one first.
Lesson 2 in this module, since this issue is a specific instance of the “missing required argument” category of failure covered there. WooCommerce active alongside the MCP Adapter is not required to follow along, the interesting part of this story is that it turned out not to matter.
Step 1: The report
Issue #135 was filed against a
site running WordPress 6.9.1, MCP Adapter v0.4.1, and WooCommerce 10.5.2. The reporter’s
plugin registered an ability on wp_abilities_api_init exactly the way the docs show,
and wp_register_ability() returned null. Crucially, the reporter had confirmed
doing_action( 'wp_abilities_api_init' ) returned true inside their own callback, so
this wasn’t the wrong-hook mistake from Lesson 2. Something else was going on.
Step 2: The hypothesis
The reporter noticed WooCommerce was also hooking wp_abilities_api_init, and that
WooCommerce’s own abilities registered successfully while theirs did not. Their
conclusion, reasonable given the evidence in front of them: “the registry appears to
already be initialized before our plugin’s callback runs, possibly due to WooCommerce
triggering WP_Abilities_Registry::get_instance() first.” In other words, a suspected
load-order conflict between WooCommerce’s own copy of the Abilities API machinery and
WordPress core’s native implementation in 6.9.
This is worth sitting with. The reporter’s theory used real class names, a real version-specific detail (WooCommerce bundling its own abilities-api code ahead of core support landing in 6.9), and a real observed asymmetry (one plugin’s abilities registered, the other’s didn’t). Every piece of evidence was consistent with the WooCommerce-conflict theory. It was still wrong.
Step 3: What the thread actually found
Working through the issue’s comments in order tells the real story. One commenter,
stefanolissa, pointed at something much more basic: “Abilities need a category and
must be registered during the wp_abilities_api_categories_init action; probably, that
is the problem.” Another commenter, karthikeya-io, tested it directly: adding
'category' => 'site' to the reporter’s exact reproduction code made the ability show
up in wp_get_abilities() immediately. The original reporter confirmed it fixed the
issue and the thread closed on that basis.
The reporter’s original reproduction code, copied from an outdated pattern that
predated category becoming a required argument, simply never set one. WooCommerce’s
abilities registered fine because WooCommerce’s own registrations included a category.
The presence of WooCommerce in the environment was a coincidence of timing in the bug
report, not a cause of the bug.
Step 4: Why the fix was documentation, not code
PR #224, “docs: document required
category field for abilities,” closed the issue. It didn’t touch the adapter’s
registration logic at all, because there was nothing to fix in the code: core’s
Abilities API already correctly required a category and already correctly returned
null when one was missing. What was actually broken was the documentation and
example code developers were copying, which predated the category requirement and
never mentioned it. The PR added the required field to every example, added a section
explaining core’s default site and user categories plus how to register your own,
and added explicit troubleshooting guidance for the null-return failure mode. As one
of the commenters on the thread put it, this had been “a very common stumbling block,”
common enough that the actionable fix was making sure nobody else hit it by copying the
same outdated examples.
If you’re running WooCommerce (or any large plugin) alongside your own abilities and
something isn’t registering, resist reaching for a plugin-conflict theory before
you’ve confirmed the basics from Lesson 2: correct hook, no duplicate name, and every
required argument present, category chief among them. The real issue #135 thread is a
direct, citable example of a multi-comment debugging effort that started with a
plausible-sounding conflict theory and ended with one missing string.
Test it: confirm your own category is real, not just present
A missing category returns null from wp_register_ability(). But a category that’s
present in your code yet was never actually registered fails exactly the same way,
since wp_register_ability_category() has to run on wp_abilities_api_categories_init
before your ability’s registration on wp_abilities_api_init even attempts to use it.
// File: my-plugin.php
add_action( 'wp_abilities_api_categories_init', function () {
wp_register_ability_category( 'my-plugin', array(
'label' => __( 'My Plugin', 'my-plugin' ),
) );
} );
# File: terminal, confirm the category itself exists before blaming the ability
wp eval '
$categories = wp_get_ability_categories();
echo isset( $categories["my-plugin"] ) ? "category exists" : "category NOT registered, register it on wp_abilities_api_categories_init";
'
Recap
Issue #135 looked like a WooCommerce load-order conflict and was reported that way, in
good faith, with real evidence pointing that direction. The actual root cause, found by
the community and confirmed by testing, was a missing category argument, unrelated
to WooCommerce entirely. PR #224 resolved it by fixing the documentation and examples
that had let the mistake propagate, not by changing any registration code. When you’re
debugging a registration failure that seems to involve another large plugin, confirm
the boring causes from Lesson 2, especially category, before investing time in a
theory about plugin load order.
Resources & further reading
- Issue #135, wp_register_ability returns NULL when called from plugin during wp_abilities_api_init, GitHub
- PR #224, docs: document required category field for abilities, GitHub
- wp_register_ability_category() function reference, developer.wordpress.org
- Abilities API reference, developer.wordpress.org