AIForum.

How to Debug WordPress REST API Errors When an AI Ability Fails

On this page

THE SHORT ANSWER

When an AI Ability fails over the WordPress REST API, the error usually tells you a request was rejected without telling you which layer rejected it. Debug by testing the request as a stack of independent gates — route, authentication, exposure, permission, input schema, and the ability's own execution — in that order.
Quick facts

When an AI Ability fails over the WordPress REST API, the error message is usually accurate but incomplete: it tells you the request was rejected without telling you which layer rejected it. The fastest way to debug is to treat the request as a stack of independent gates — route, authentication, exposure, permission, input schema, and the ability’s own execution — and test them in that order, from cheapest to most invasive.

This guide is for developers and site operators running the WordPress AI plugin’s Abilities API endpoints, typically against a local or staging install. It assumes you can reach the site over HTTP and that you have an administrator account.

Why AI Ability failures look confusing

An ability is registered in PHP. The REST layer is a thin adapter on top of it. When you call the run endpoint, WordPress must first match the URL to a registered route, then resolve the current user, then call the ability’s permission_callback(), then validate the JSON body against the ability’s input schema, and only then hand control to the ability itself. Any of those stages can produce a failure, and several of them return similar-looking JSON.

Two facts from the official Abilities API REST documentation shape the whole debugging process. First, access to all Abilities REST API endpoints requires an authenticated user, and access to execute an individual ability is further restricted by that ability’s permission_callback(). Second, registered abilities are not exposed over REST by default: show_in_rest defaults to false, and an ability that is hidden returns a rest_ability_not_found error if you try to reach it over HTTP — even though it still works internally through wp_get_ability() and $ability->execute().

Step 1: Confirm the route and namespace

Start by confirming you are hitting a route that exists. The Abilities API endpoints live under the /wp-abilities/v1 namespace, which in a standard WordPress install means URLs beginning with /wp-json/wp-abilities/v1/. The plugin’s testing documentation gives the concrete shape of an execute call:

curl -X POST "https://wordpress-ai.test/wp-json/wp-abilities/v1/abilities/ai/excerpt-generation/run" 
  -u "your-username:application-password" 
  -H "Content-Type: application/json" 
  -d '{
    "input": {
      "content": "This is a sample article about artificial intelligence and machine learning."
    }
  }'

If this returns rest_no_route, the URL is wrong, not the ability. Common causes: a missing /wp-json prefix, a site configured with plain permalinks where the REST route is expressed through ?rest_route=, a security plugin or reverse proxy stripping the namespace, or a typo in the ability name. Check the route with a GET to the ability listing endpoint rather than assuming the execute URL is correct.

If the route resolves but the response is rest_ability_not_found, the URL is fine and the ability is simply not exposed. That is a different problem, handled in Step 3.

Step 2: Confirm authentication

All Abilities REST endpoints require an authenticated user. An unauthenticated request will not reach the ability at all, so a 401 is never evidence that the ability is broken.

The documentation recommends Application Passwords for testing. Create one under Users → Your Profile → Application Passwords, copy the generated value (it is shown only once), and pass it with curl’s -u flag as username:application-password. Application Passwords are the cleaner option for scripted debugging because they do not expire with a browser session and do not require a nonce.

The alternative is cookie authentication with a nonce. Log into wp-admin in a browser, read wpApiSettings.nonce from the DevTools console if it is available, copy the wordpress_logged_in_[hash] cookie value, and send both:

curl -X POST "https://wordpress-ai.test/wp-json/wp-abilities/v1/abilities/ai/excerpt-generation/run" 
  -H "Content-Type: application/json" 
  -H "X-WP-Nonce: YOUR_NONCE_HERE" 
  -H "Cookie: wordpress_logged_in_[hash]=YOUR_COOKIE_VALUE_HERE" 
  -d '{ "input": { "content": "..." } }'

Cookie authentication fails in ways Application Passwords do not: a stale nonce, an expired session, or a cookie copied from the wrong domain all produce authentication errors that look like permission errors. If you are debugging, use Application Passwords first and only fall back to cookies when you specifically need to reproduce a browser session. Never paste real credentials or cookie values into a public issue report — mask them.

Step 3: Check exposure and permission separately

These two failures are the most commonly conflated, and they have different fixes.

Exposure is controlled at registration time by the show_in_rest meta. With show_in_rest => true, the ability appears in REST listings and can be executed over HTTP. With show_in_rest => false — the default — it is hidden from listings and cannot be executed over REST; requests return rest_ability_not_found. If you get that error, the ability is registered but not exposed. The fix belongs in the registration code, not in your curl command.

Permission is controlled by the ability’s permission_callback(), which runs after the user is resolved and after the route is matched. A logged-in user who lacks the required capability will be rejected here even though authentication succeeded. This is the stage where a 403 usually originates. Note that permission_callback() is per-ability: two abilities in the same namespace can have different requirements, so a request that works for one may legitimately fail for another.

A useful discriminator: if the ability is missing from the REST listing entirely, suspect exposure. If it appears in the listing but execution fails, suspect permission or input.

Step 4: Validate the input payload

The execute endpoint expects a JSON body with an input object, and the ability validates that object against its own input schema. A malformed body, a missing required field, or a wrong type produces a validation error rather than an execution error.

Two practical checks. First, confirm Content-Type: application/json is set; without it, WordPress may not parse the body at all. Second, confirm the top-level shape. In the documented example the payload is {"input": {"content": "..."}} — the content string is nested inside input, not at the top level. Sending {"content": "..."} is a common mistake and fails schema validation before the AI code ever runs.

If you are unsure what the ability expects, retrieve the ability definition from the REST API and read its input schema rather than guessing from the error message.

Step 5: Isolate REST from the ability itself

If route, authentication, exposure, permission, and input all check out and the request still fails, the problem is probably inside the ability. The documentation notes that abilities remain available for internal PHP usage via wp_get_ability() and $ability->execute() regardless of their REST exposure.

That gives you a clean isolation test: call the ability directly in PHP — through WP-CLI eval, a small mu-plugin, or a test harness — and pass the same input. If the direct call also fails, the bug is in the ability or in a dependency it uses, such as an upstream AI provider, a missing API key, or a timeout. If the direct call succeeds but the REST call fails, the problem is in the adapter layer: routing, authentication, exposure, permission, or serialization. This single test eliminates most guesswork.

A worked diagnostic sequence

The following is an illustrative walkthrough, not a recorded test run. Suppose a POST to the excerpt-generation run endpoint returns a 403 with a permission-style error.

  1. Re-run with -v to capture the status line and response body. Confirm the HTTP status, because 401 and 403 point to different stages.
  2. GET the ability listing. If the ability is absent, the cause is exposure (show_in_rest). If it is present, continue.
  3. Confirm which user the Application Password belongs to and whether that user has the capability the ability’s permission_callback() requires. A password created for a low-privilege account will authenticate successfully and still be rejected.
  4. If permission is satisfied, retrieve the ability definition and compare your JSON body against its input schema field by field.
  5. If the payload matches, run the ability directly via wp_get_ability() with the same input to determine whether the failure is in the ability or in the REST adapter.

At each step, the result narrows the candidate layers rather than confirming a guess.

Verifying the fix

Re-run the original curl command unchanged. A successful execute response confirms the full path — route, authentication, permission, schema, and execution — is working. If you changed the registration code to expose the ability, also confirm it now appears in the REST listing; that is the observable check that show_in_rest took effect.

Limitations and what the documentation does not cover

The official documentation describes the endpoint contract, the authentication methods, and the exposure and permission model. It does not enumerate every error code an individual ability may return, because those come from the ability’s own implementation and its upstream dependencies. When the error originates inside the ability, the REST error response is a wrapper around a message the ability chose — so the underlying cause may need to be found in the plugin’s logs or in the AI provider’s response, not in the REST layer.

Version matters too. The Abilities API and the AI plugin are both under active development, and route names, meta keys, and authentication guidance can change between releases. Verify behavior against the documentation for the version you are running rather than against a tutorial written for an older build.

Where to go next

Keep a short diagnostic record for each failure: the exact URL, the HTTP status, the response body, the authentication method, and whether the ability appears in the REST listing. That record turns a vague “the AI ability is broken” report into something the community can actually help with. If you are stuck, share the ability name, your WordPress and plugin versions, the masked request, and the full error response — and say whether the direct wp_get_ability() call succeeds.

FAQ

Why does my request return rest_ability_not_found?

Because the ability is registered but not exposed over REST. show_in_rest defaults to false, so the ability is hidden from listings and cannot be executed over HTTP. Register it with show_in_rest => true, or call it internally via wp_get_ability().

The ability works in PHP but fails over REST. Why?

The REST layer adds route matching, authentication, exposure, permission, and schema validation on top of the ability. Check whether the ability appears in the REST listing and whether the authenticated user passes the ability's permission_callback().

Do I need a nonce when using an Application Password?

No. Application Passwords are used with basic authentication via curl's -u flag and do not require the X-WP-Nonce header. Nonces belong to cookie authentication.

Does a 401 response mean the ability is broken?

No. All Abilities REST endpoints require an authenticated user, so a 401 means the request never reached the ability. Investigate the credentials before investigating the ability code.

References

  1. https://github.com/WordPress/ai/blob/develop/docs/TESTING_REST_API.md
  2. https://developer.wordpress.org/apis/abilities-api/rest-api-endpoints/

Related terms

Ability
A registered unit of functionality in the WordPress Abilities API that can be executed internally in PHP or, when exposed, over REST.
show_in_rest
Registration meta that controls whether an ability appears in REST listings and can be executed over HTTP. It defaults to false.
permission_callback()
The per-ability callback that determines whether the currently authenticated user is allowed to execute that ability.
Application Password
A WordPress credential used to authenticate REST requests without a browser session, passed with curl's -u flag.
Nonce
A short-lived token used with cookie authentication, sent in the X-WP-Nonce header. It is not required when using Application Passwords.
rest_no_route
A WordPress REST error indicating that no registered route matched the requested URL.
rest_ability_not_found
The error returned when an ability is accessed over REST but is not exposed, typically because show_in_rest is false.

Leave a Reply

Your email address will not be published. Required fields are marked *