Files
mcphub/docs/media-error-codes.md
airano-ir f203ca88de
Some checks failed
Release / Test before release (push) Has been cancelled
Release / Publish to PyPI (push) Has been cancelled
Release / Publish to Docker Hub (push) Has been cancelled
Release / Create GitHub Release (push) Has been cancelled
feat(v3.12.0): media pipeline, AI image generation, capability probe, companion v2.9.0
Three-month batch sync from internal repo (~80 commits) covering Tracks F.5a, F.7e, F.8, F.17, F.18, F.X.

WordPress media pipeline
- Pillow-based optimization, AI image generation (OpenAI / Stability / Replicate / Google Nano Banana / OpenRouter), chunked + resumable uploads, bulk delete/reassign, idempotent retries.

Capability discovery (F.7e)
- Per-site credential probe + adapters for WordPress / WooCommerce / Gitea, tier-fit unions granted ∪ roles, capability badge UI with HTMX partial re-check, install hint in every companion-unreachable error.

Companion plugin overhaul
- Renamed wordpress-plugin/airano-mcp-seo-bridge → wordpress-plugin/airano-mcp-bridge.
- Eight new endpoints: /capabilities, /bulk-meta, /export, /cache-purge, /transient-flush, /site-health, /audit-hook, /upload-and-attach.
- wp.org Plugin Check pass: i18n, WP_Filesystem, scheme allowlist on audit-hook URL.

Other
- Gitea ergonomics (F.17): batch files, tree, search, compare, releases, fork.
- Opportunistic bcrypt upgrade for legacy SHA-256 admin keys (F.8).
- n8n refactor: structured errors, capability probe, missing tools backfilled.
- Idempotency-Key dedup for AI media upload retries; WP client fast-fails on unreachable sites.

Docs
- README + CLAUDE.md drop the fixed "633 tools" claim. The total grows with each release; per-plugin approximations + dashboard-surfaced counts replace it.
- Tools/Tests badges removed in favour of "Plugins: 10".

Deployment
- PyPI mirror chain, optional BUILD_HTTP_PROXY, Alpine→Yandex apk mirror, Debian-slim Plan-B Dockerfile, mirror.gcr.io variant.

CI
- Black + Ruff clean on Python 3.12; pytest tests/ green.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 16:25:58 +02:00

6.8 KiB

Media Upload Error Codes (F.5a.6.2)

All media-upload tools return structured JSON errors shaped as:

{
  "error_code": "TOO_LARGE",
  "message": "File is 12345678 bytes; limit is 10485760 bytes ...",
  "details": { "size": 12345678, "max": 10485760 }
}

The error_code values below are stable: renaming or removing one is a breaking change. The source of truth is core/media_error_codes.py (the set MEDIA_ERROR_CODES) and the stability test in tests/plugins/wordpress/test_media_error_taxonomy.py.

Input / validation

Code When it fires
BAD_BASE64 The supplied base64 payload (full upload or single chunk) fails to decode.
BAD_MODE mode argument to attach tools is not append or replace.
BAD_ROLE role argument to attach tools is not main or gallery.
BAD_SIZE Chunked total_bytes is <= 0.
BAD_SOURCE Attach-upload helper given a source other than base64 / url.
EMPTY_FILE Decoded payload is zero bytes.
MEDIA_NOT_FOUND A supplied media_id does not exist in the WP media library.
MIME_REJECTED Sniffed MIME is not in the allow-list (ALLOWED_MIMES).
MISSING_FIELD A required field is missing (e.g. data for base64, url for URL sideload).
SSRF URL resolves to private/loopback/link-local/metadata IP or is on the host blocklist.
TOO_LARGE Payload exceeds WP_MEDIA_MAX_MB / streamed download exceeds the byte cap.
URL_FETCH_FAILED Remote URL returned >= 400 while downloading.

WordPress REST upstream

Code When it fires
WP_413 WordPress rejected the upload with HTTP 413 (server upload_max_filesize too low).
WP_AUTH WordPress rejected auth (401/403). Application Password likely invalid/expired.
WP_BAD_RESPONSE Upload accepted but WP returned a non-JSON body.
WP_CREDENTIALS_MISSING (WC sites only) Tool needs a WP Application Password to hit /wp/v2/media. WC Consumer Key + Secret do not authenticate the WP core REST. Add wp_username + wp_app_password in Connection Settings → advanced.
WP_<status> Any other non-2xx status from WP — e.g. WP_400, WP_500. Dynamic.

Companion plugin upload-chunk route (F.5a.7)

These only fire when MCPHub chose the companion /airano-mcp/v1/upload-chunk route (because the probe advertises the helper and the payload exceeds upload_max_filesize). A companion failure is non-fatal: MCPHub falls back to the standard /wp/v2/media route on any error here, so these codes usually surface in logs, not to end users.

Code When it fires
COMPANION_BAD_RESPONSE Companion route returned 2xx but the body was not parseable JSON.
COMPANION_<status> Any non-2xx status from the companion route — e.g. COMPANION_500.

Chunked upload session

Code When it fires
BAD_STATE Session exists but is not in open state (already finalized/aborted).
CHECKSUM_MISMATCH Assembled sha256 does not match value supplied at start.
CHUNK_CHECKSUM Per-chunk sha256 does not match the value supplied with the chunk.
CHUNK_ORDER Chunk index does not match next_chunk.
CHUNK_OVERFLOW Appending this chunk would exceed declared total_bytes.
EXPIRED Session's TTL has elapsed.
INCOMPLETE Finalize called before all declared bytes arrived.
NO_SESSION session_id is unknown.
QUOTA_EXCEEDED User already has MCPHUB_UPLOAD_MAX_CONCURRENT open sessions.
SESSION_TOO_LARGE Declared total_bytes exceeds the hard session cap (default 500 MB).

AI generation providers

Code When it fires
GENERATION_FAILED Generic provider failure not covered by a more specific code.
NO_PROVIDER_KEY No per-user key stored and no env fallback for the selected provider.
PROVIDER_AUTH Provider rejected the supplied API key.
PROVIDER_BAD_REQUEST Provider returned 4xx — prompt or size was invalid.
PROVIDER_BAD_RESPONSE Provider returned a 2xx with an unexpected shape.
PROVIDER_QUOTA Provider returned 429 / quota error.
PROVIDER_TIMEOUT Provider timed out.
PROVIDER_UNAVAILABLE Provider returned 5xx / circuit open.
PROVIDER_UNKNOWN provider argument is not one of the registered providers.

Rate / policy

Code When it fires
TOOL_RATE_LIMITED Per-tool, per-user cap exceeded (see core/tool_rate_limiter.py).

Catchall

Code When it fires
INTERNAL Unexpected exception reached the top of a tool handler. Details in message.