Error code reference
Nội dung này hiện chưa có sẵn bằng ngôn ngữ của bạn.
Errors from the MCP server come in two shapes:
- HTTP-level: returned with a non-2xx status from
/mcpor/admin/api/*. Usually auth-related (401, 403). - Tool result:
{ "content": [...], "isError": true }ontools/call. The text content has the format<code>: <message>. Every tool call also writes anaudit_logrow with the code in theerrorcolumn.
The codes below are stable — Claude (and other clients) can switch on them in retry logic.
Auth & scoping
Section titled “Auth & scoping”E_AUTH
Section titled “E_AUTH”HTTP 401 Unauthorized. Returned when the bearer or OAuth token can’t be
verified.
| Trigger | Fix |
|---|---|
Authorization header missing | Add Authorization: Bearer <token>. |
OAuth oat_* token expired (default 1h) | Let Claude refresh — auto for Custom Connector; manual re-auth otherwise. |
OAuth oat_* revoked | Re-add the connector in Claude Desktop. |
Bearer sn_prod_* not in D1 | Token was revoked or never existed. Mint a fresh one at /admin/tokens. |
Master token used on /mcp | The master token is for /admin/* only. Mint a regular token. |
The response also carries WWW-Authenticate: Bearer resource_metadata="…/.well-known/oauth-protected-resource"
so MCP clients can autodiscover OAuth.
E_SCOPE_DENIED
Section titled “E_SCOPE_DENIED”HTTP 403 Forbidden. The token is valid but doesn’t include the requested
site or tool.
| Trigger | Fix |
|---|---|
site_id not in tokens.allowed_sites (or owner’s user_sites) | /admin/tokens → edit → tick the site. Or for OAuth, grant the site to the user at /admin/users. |
Tool name not in tokens.allowed_tools | /admin/tokens → edit → tick the tool. |
See Tokens & scoping for the full scope-check algorithm.
E_SUPER_ADMIN_ONLY
Section titled “E_SUPER_ADMIN_ONLY”HTTP 403 Forbidden. A regular-user token tried to call a tool restricted
to super-admin (master-token-issued) bearer tokens.
The only tool in this category today is delete_astro_route. See
Architecture — hard rule.
Fix: mint the bearer token from the Master token tab of /admin/login
instead of a user account. Tokens minted from the master tab have
owner_user_id IS NULL which is the runtime gate.
E_FORBIDDEN
Section titled “E_FORBIDDEN”HTTP 403 Forbidden. Admin-side check — usually “super-admin only” for the
admin API (e.g. user management).
Fix: log in as super-admin via the master token.
E_FORBIDDEN_OP
Section titled “E_FORBIDDEN_OP”HTTP 403 Forbidden. The adapter refused a payload that would violate the
no-publish-now / no-delete rule.
| Trigger | Fix |
|---|---|
status=publish on WP post/page create or update | Use schedule_draft / schedule_page with a future ISO8601 time. |
Duda POST /publish without schedule_publish_date | Same — schedule, don’t publish-now. |
scheduled_at within 60s of now | Pick a time ≥ 60s in the future. |
DELETE on a WP post or page | This server doesn’t expose delete tools. Delete in wp-admin. |
Input validation
Section titled “Input validation”E_BAD_INPUT
Section titled “E_BAD_INPUT”HTTP 400 Bad Request. Argument missing, wrong type, or fails an enum check.
The message names the offending field — e.g. title is required,
scheduled_at must be a valid ISO8601 string, tags must be string[].
Fix: correct the argument shape. Tool input schemas are in
/reference/tools/ — Claude usually self-corrects after
one round-trip.
E_BAD_ROUTE
Section titled “E_BAD_ROUTE”HTTP 400 Bad Request. An Astro route doesn’t satisfy validation rules.
Bad routes include:
- Contains
..(path traversal). - Contains
[,],\,?,#,%. - Empty segments (
/foo//bar). - Segments with leading/trailing dashes, or non-alphanumeric chars.
- Dynamic-route brackets (
/[slug]) — explicit routes only for v1.
Fix: rephrase the route. Allowed format per segment:
[a-z0-9][a-z0-9-]*[a-z0-9]? (case-insensitive).
E_DISALLOWED_CONTENT
Section titled “E_DISALLOWED_CONTENT”HTTP 400 Bad Request. The create_page_from_code / update_page_from_code
validator caught one of:
<script src="…">in HTML — inline JS only.<iframe>in HTML — banned for cache-plugin safety.@import url(https://…)in CSS — bypasses the scope rewriter.
Fix: inline the asset, or host it via the theme <head> and reference by
selector. See Code-first pages — validation rules.
E_PAYLOAD_TOO_LARGE
Section titled “E_PAYLOAD_TOO_LARGE”HTTP 413 Payload Too Large. Size cap exceeded on one of:
html> 200 KBcss> 100 KBjs> 100 KB
Fix: split the page; lazy-load below-the-fold sections; or move heavy assets
out to <link> / <img src> referencing the theme’s media library.
E_MISSING_PLACEHOLDER
Section titled “E_MISSING_PLACEHOLDER”HTTP 400 Bad Request. Elementor template duplicate was called with a
variables map that doesn’t cover every {{KEY}} the template references.
The message includes the missing keys: Missing variables: HERO_SUB, CTA_LINK.
Fix: add the missing entries to variables. To see what a template requires,
call list_page_templates.
E_MISSING_FRONTMATTER
Section titled “E_MISSING_FRONTMATTER”HTTP 400 Bad Request. save_md_to_github received markdown that doesn’t
start with a --- YAML frontmatter block.
Fix: prepend the YAML header. Minimum:
---title: ...slug: ...---Site / builder / platform
Section titled “Site / builder / platform”E_NOT_SUPPORTED
Section titled “E_NOT_SUPPORTED”HTTP 400 Bad Request. A tool was called on a site whose platform
doesn’t support that operation.
| Trigger | Fix |
|---|---|
create_page_from_code on Duda or Astro | WordPress only. |
duplicate_page on WordPress | Duda only. |
inject_page_section on WordPress | Duda only. |
list_taxonomies / create_taxonomy on Duda | WordPress only. |
| Post / page tools on Astro | Use the Astro tools instead. |
create_page_draft on Astro | Use create_astro_route. |
diagnose_site on non-WordPress | The diagnostics are WP-specific. |
E_BUILDER_REQUIRED
Section titled “E_BUILDER_REQUIRED”HTTP 400 Bad Request. The WP site has multiple builders enabled, and the
caller didn’t specify page_builder.
Fix: pass page_builder: "gutenberg" or "elementor". To discover which
builders are enabled, call list_builders_for_site.
E_BUILDER_NOT_AVAILABLE
Section titled “E_BUILDER_NOT_AVAILABLE”HTTP 400 Bad Request. page_builder was set to a builder that isn’t in
the site’s available_builders list.
Fix: either pick an available builder, or open /admin/sites → edit and
tick the missing builder.
E_BUILDER_MISMATCH
Section titled “E_BUILDER_MISMATCH”HTTP 400 Bad Request. deploy_md_to_site was asked to render to a target
site where Elementor is the only available builder. Markdown can’t generate
an Elementor JSON layout.
Fix: either enable Gutenberg on the target site, or call create_page_draft
directly with elementor_template_id + variables.
E_BAD_ELEMENTOR_DATA
Section titled “E_BAD_ELEMENTOR_DATA”HTTP 500 Internal Server Error. The template’s _elementor_data postmeta
isn’t valid JSON.
Fix: open the template in wp-admin → Elementor → save again (re-serializes the JSON). If it persists, the postmeta has likely been hand-edited.
GitHub
Section titled “GitHub”E_GITHUB_NOT_LINKED
Section titled “E_GITHUB_NOT_LINKED”HTTP 409 Conflict. The site has no github_repo configured.
Fix: /admin/sites → edit → set github_repo (format owner/repo).
E_GITHUB_NOT_INSTALLED
Section titled “E_GITHUB_NOT_INSTALLED”HTTP 409 Conflict. No GitHub App installation exists for the repo’s owner
account.
Fix: /admin/github → Install on org/user. Pick the account that owns
the repo.
E_NOT_MCP_MANAGED
Section titled “E_NOT_MCP_MANAGED”HTTP 409 Conflict. update_astro_route / delete_astro_route was called
on a .astro file that was NOT created by this server (no marker comment).
The marker is Generated by SEO Navigator MCP in the file’s frontmatter
comments. Pass force: true to overwrite/delete a hand-written file anyway.
E_SHA_MISMATCH
Section titled “E_SHA_MISMATCH”HTTP 409 Conflict. You passed an sha for optimistic concurrency, but
the file’s current sha on the branch is different (concurrent edit).
Fix: re-read the file (list_astro_routes or read_md_from_github) and
retry with the fresh sha.
E_ROUTE_EXISTS
Section titled “E_ROUTE_EXISTS”HTTP 409 Conflict. create_astro_route would overwrite an existing file.
Fix: call update_astro_route
instead. It refuses non-MCP-managed files by default — pass force: true
to overwrite anyway.
Cloudflare Pages
Section titled “Cloudflare Pages”E_CF_PAGES_NOT_CONFIGURED
Section titled “E_CF_PAGES_NOT_CONFIGURED”HTTP 503 Service Unavailable. check_astro_deploy was called but the
worker has no CLOUDFLARE_API_TOKEN + CLOUDFLARE_ACCOUNT_ID secrets.
Fix: super-admin sets them with wrangler secret put. See
Deploy — secrets.
The tool normally hides itself from tools/list when the secrets are
missing, so this only fires if a client invokes it by name regardless.
E_CF_PAGES_PROJECT_MISSING
Section titled “E_CF_PAGES_PROJECT_MISSING”HTTP 409 Conflict. The site is an Astro site but has no cf_pages_project
set.
Fix: /admin/sites → edit → set cf_pages_project to the slug shown in
Cloudflare Pages (e.g. marketing-site).
Upstream
Section titled “Upstream”E_UPSTREAM
Section titled “E_UPSTREAM”HTTP 4xx or 5xx mirroring the upstream status (clamped to 400–599).
The adapter contacted WP / Duda / GitHub / Cloudflare Pages and got a
failure response.
The error message includes the upstream HTTP status and a body excerpt:
E_UPSTREAM: WP 400 — Invalid term id: 0.
Common causes:
- WP 400 invalid term id — usually a tag/category mismatch. The worker
auto-resolves names → IDs in
deploy_md_to_siteand the regular post tools, but if you bypass that path and pass an integer that doesn’t exist, you’ll see this. - WP 401 — Application Password wrong or revoked. Re-issue from wp-admin → Users → Profile.
- WP 403 — App Password user lacks
edit_pages/edit_posts. Grant the role in wp-admin. - WP 404 —
post_id/page_iddoesn’t exist on this site, or it’s a Duda ID being used as a WP ID. Always pairsite_id+post_id/page_id. - Duda 401 —
DUDA_API_USER/DUDA_API_PASSwrong. The worker uses a single shared credential pair across all Duda sites. - Duda 404 site_alias —
duda_site_namedoesn’t match the Duda site’s alias. Open the Duda dashboard while editing the site; the URL contains the alias (e.g.7a1c9e2f). - GitHub 401 — the cached installation token expired or the GitHub App
was uninstalled. Visit
/admin/githubto reinstall. - GitHub 422 path is too long — GitHub limits paths to 250 chars. Move to a shallower folder.
- Cloudflare 1xxx error — usually a transient origin firewall block. Retry; if persistent, the site’s WAF is rejecting the worker’s request.
E_INTERNAL
Section titled “E_INTERNAL”HTTP 500 Internal Server Error. Something inside the worker hit an
“impossible” branch (typically a credential kind mismatch — Duda creds
appearing where the code expected WP creds).
Fix: this should never happen at runtime. If you see it, file an issue on GitHub with the audit row.
Data not found
Section titled “Data not found”E_NOT_FOUND
Section titled “E_NOT_FOUND”HTTP 404 Not Found. Looked up a record that doesn’t exist.
| Trigger | Fix |
|---|---|
site_id not in D1 sites table | /admin/sites → add it, or check the slug. |
read_md_from_github path doesn’t exist | Run list_md_from_github to see what’s there. |
update_astro_route / delete_astro_route on a route that doesn’t exist | Use create_astro_route instead. |
deploy_md_to_site source file missing | Same as read_md_from_github. |
Tool denied (not a code, but worth knowing)
Section titled “Tool denied (not a code, but worth knowing)”When the token’s allowed_tools doesn’t include a tool, the worker returns
a JSON-RPC tool result { isError: true, content: "Tool 'X' not allowed for this token" } with audit status=denied. This is the same shape as
E_SCOPE_DENIED from the user’s perspective — fix is identical: edit token
scope.
Proxy tools matching the destructive guardrail
((delete|remove|trash|destroy)(page|post|template|content|media) or
publish-* except unpublish-*) are filtered out of tools/list and
refused at tools/call, regardless of the token’s allowed_tools. There’s
no way to enable these — see
Architecture — hard rule.
How to debug
Section titled “How to debug”- Open
/admin/audit. Filter bystatus=errorand the time range. Each row has the full args and error string. - Cross-reference
tool+erroragainst the table above. - For upstream errors, the message includes the body excerpt — useful when WP returns a custom JSON shape (e.g. WAF blocking a specific header).
- For repeated
E_AUTH, checktokens.revoked_atin D1 (wrangler d1 execute blog_mcp --remote --command "SELECT id, name, revoked_at FROM tokens").
For anything not covered here, the source of truth is src/utils/errors.ts
grep -r "new AppError" src/in the code repo.