# Undo Token Errors

> Fix every Royal MCP Pro undo error: token_not_found, token_consumed, token_expired, undo_handler_missing, undo_handler_failed, and insufficient_caps.

- Canonical: <https://royalplugins.com/support/royal-mcp-pro/undo-token-errors/>
- HTML version: <https://royalplugins.com/support/royal-mcp-pro/undo-token-errors/>

---

You called `royal_mcp_undo_last_operation` with the token from a prior write, but it returned an error instead of reversing the operation. This doc lists every error code the undo tool returns, why it fires, and how to fix it. The error code is always in the response's `content[0].text` field prefixed by the code name (e.g. `token_expired: Undo token has expired.`).

How undo tokens work at a glance

- **Every destructive Pro tool** returns an `undo` object in its response envelope: `{token, expires_at, ttl_hours}`.
- **Default TTL** is 72 hours. Some tool families override to longer (SEO / Widget / Redirection bulk writers use 168h, i.e. 7 days).
- **Tokens are one-shot** — consumed on the first successful undo. Subsequent calls with the same token fail.
- **Cap check** requires you to be the original actor who ran the tool, OR to hold `royal_mcp_pro_undo_any`, OR to have `manage_options`, OR to be a super-admin (multisite).
- **Read-only tools** (like `wp_audit_seo_bulk`) return a no-op undo token for API consistency — redeeming it is a no-op success, not an error.

## The 6 error codes

### token\_not\_found

The token you passed doesn't exist in the undo store. Either you never had one to begin with (empty `undo_token` arg), the string is wrong (typo, truncated, extra whitespace), or the row was already purged by the database's retention job.

#### Fix

- **Empty arg:** Undo tool requires the `undo_token` argument. If you called `royal_mcp_undo_last_operation` with no arg, or your MCP client dropped the field silently, that fires this error.
- **Wrong token:** The correct token is the 64-character hex string in the `undo.token` field of the tool response you're trying to reverse. Not the `audit_id`, not the tool name, not `expires_at`.
- **Purged row:** A daily sweep deletes undo rows that are either consumed OR expired more than 30 days ago (filter: `royal_mcp_pro_undo_retention_days`, default 30). Tokens from writes more than ~a month past their TTL are genuinely gone from the store — consider a SiteVault restore instead. If the write is recent but the token itself is truly past-TTL and still in the store, you'll see `token_expired` instead.

### token\_consumed

The token was valid, but somebody (possibly you, in a prior session) already used it. Undo tokens are one-shot; once redeemed, they can't be redeemed again.

#### Fix

Check the site's Audit Log — a successful undo of this token would have written a row indicating the operation was undone. If you're trying to reverse an operation that was already reversed, the state is what you wanted; no further action needed.

If the audit log doesn't show an undo but the token is marked consumed, that's unusual. Include the `audit_id` from the token error's structured content in a support email so we can trace what happened.

### token\_expired

The token is real and was never consumed, but the current time is past its `expires_at`. Undo runway is 72h by default (168h for SEO / Widget / Redirection bulk).

#### Fix

Undo is no longer possible via the token path. For most Pro tools, you have three recovery options:

- **SiteVault restore** if you took a backup before the operation. Match on the timestamp shown in the original write's audit-log row.
- **Manual reverse** using the Pro tool's inverse operation (e.g. `wp_bulk_update_products` can reverse a prior price update by writing the old prices back).
- **WordPress revision restore** for post-content writes — every Pro tool that modifies post\_content also writes a revision, viewable in wp-admin under Revisions on the affected post.

The 72h/168h TTL is a hard limit designed to keep the undo store from growing unbounded — not configurable per-request.

### undo\_handler\_missing

The token is valid and unconsumed, but no undo handler is registered for the tool that created it. This should never happen in a normal Royal MCP Pro install — every destructive Pro tool registers its own undo handler at plugin bootstrap.

#### Fix

- **Confirm Royal MCP Pro is active** — if the plugin was deactivated after the tool ran but before you tried to undo, the handler isn't loaded and this error fires. Re-activate the plugin, then retry.
- **Confirm you're on the same major version** — if you upgraded / downgraded Royal MCP Pro between the write and the undo attempt and the tool's handler moved / renamed across versions, the old token's `tool_name` may not match any current handler. Rare, but happens on rapid upgrade cycles. Contact support with the `tool_name` from the error response.

### undo\_handler\_failed

The handler was invoked but threw an exception while trying to reverse the operation. Common cause: the data state changed since the original write (rows deleted by another process, referenced posts trashed, database schema altered) so the reverse can't safely apply.

#### Fix

The exception's message is included in the error response's `content[0].text`. Read that first — it often names the specific row / post that couldn't be restored.

Once the handler starts and fails partway through, the operation is in a half-restored state. Do NOT re-run undo with the same token — it was consumed on the first (failed) attempt, so you'd get `token_consumed` now anyway. Instead:

- Read the exception message to identify what specifically failed
- Fix the state issue if possible (restore the deleted parent post, roll back the schema change)
- Manually reverse the remaining rows using the Pro tool's inverse operation

### insufficient\_caps

You're logged in as a user who doesn't have the capability to undo this operation. The undo tool enforces one of: original actor, `royal_mcp_pro_undo_any` capability, `manage_options` capability, or super-admin (on multisite).

#### Fix

The error response includes the `original_actor_id` field in its structured content. Options:

- **Log in as the original actor** and retry (usually the AI-flow user whose credentials the MCP call ran under)
- **Grant the current user `manage_options`** if they're an admin who should be able to undo any operation. Most WordPress admin accounts already have this.
- **Grant the `royal_mcp_pro_undo_any` capability** to a specific role that isn't an admin but should be able to undo (e.g. a shop-manager-plus role). Use a capabilities plugin (Members, User Role Editor) to add the cap to that role.

The cap check is deliberately strict for operations that could touch sensitive data — letting anyone with an MCP session undo any other user's writes would be a real security issue.

## Race conditions

Two concurrent undo attempts against the same token: the first wins, the second gets whatever state the row landed in (`token_consumed` most commonly, sometimes `token_expired` if the TTL crossed during the race window). The error message will say "Undo token state changed mid-request" when the race is detected explicitly.

You don't have to do anything about race errors — they're just informing you that the operation was already handled by another process. Check the audit log to confirm the outcome.

## Where to find a token after the fact

If you didn't save the token from the original tool response and need to try again later:

1. Open the Royal MCP Pro **Audit Log** page in wp-admin
2. Find the row for the operation you want to reverse
3. Click **View Details** to expand it — the `undo_token` is stored on the row (as long as the token hasn't been purged)
4. Copy the token, then paste into an MCP call: `Call royal_mcp_undo_last_operation with undo_token="{paste}"`

Note that the audit-log row also shows the `expires_at` timestamp so you can see at a glance whether the token is still redeemable.

## Still Stuck? Two-Step Support Path

Most undo errors are self-explanatory once you know which of the 6 codes fired. If the pattern doesn't match any of the sections above, work through these two steps in order.

### Step 1: Start with the Royal MCP Troubleshooting Guide

[**Royal MCP Troubleshooting — Start Here**](https://royalplugins.com/support/royal-mcp/troubleshooting-start-here/) covers MCP-layer issues that can look like undo errors (e.g. tool call never reaching the server, response getting truncated in transit).

### Step 2: Email priority support

If MCP-layer diagnostics look clean, email priority support from your purchase email address at **[support@royalplugins.com](mailto:support@royalplugins.com)**. Priority email support is included with your license — typical response within 24 hours. **Never include your license key in email**; we look it up from your purchase address.

What to include in your email

- **The exact error code** (e.g. `undo_handler_failed`) plus the full message text
- **The original tool name** that created the token (e.g. `wc_bulk_price_update`)
- **The `audit_id`** from the error response's structuredContent (helps us trace the original write)
- **Royal MCP Pro version**
- **Which MCP client** and how the undo call was phrased
- **Time elapsed** between the original write and the undo attempt (rules token\_expired in or out)
- **Screenshot of the audit-log row** for the original write with View Details expanded

[← Upgrade Prompt with Valid License](https://royalplugins.com/support/royal-mcp-pro/pro-tool-returned-upgrade-prompt/)
[Builder Session Detected →](https://royalplugins.com/support/royal-mcp-pro/builder-session-detected-force-override/)
