Skip to content
AI & Agents
Skill

/frappe-errors-api

Use when debugging or handling API errors in Frappe/ERPNext v14/v15/v16. Prevents silent failures and wrong HTTP status codes in REST endpoints. Covers 401 Unauthorized (wrong token format, expired OAuth), 403 Forbidden (missing @whitelist, allow_guest needed), 404 Not Found

From plugin
frappe-claude-skill-package
17961 skills
Install
$ npx -y skills add Impertio-Studio/Frappe_Claude_Skill_Package --skill frappe-errors-api --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.
  • Slash command/frappe-errors-api

Context preview

The summary Claude sees to decide when to auto-load this skill.

Use when debugging or handling API errors in Frappe/ERPNext v14/v15/v16. Prevents silent failures and wrong HTTP status codes in REST endpoints. Covers 401 Unauthorized (wrong token format, expired OAuth), 403 Forbidden (missing @whitelist, allow_guest needed), 404 Not Found

SKILL.md

frappe-errors-api.SKILL.md
name: frappe-errors-api
description: >
  Use when debugging or handling API errors in Frappe/ERPNext v14/v15/v16.
  Prevents silent failures and wrong HTTP status codes in REST endpoints.
  Covers 401 Unauthorized (wrong token format, expired OAuth), 403 Forbidden
  (missing @whitelist, allow_guest needed), 404 Not Found (wrong endpoint URL),
  417 Expectation Failed (validation via frappe.throw), 500 Internal Server
  Error, CORS issues, CSRF token missing/invalid, rate limit exceeded (429),
  file upload failures, JSON parse errors in request/response, webhook delivery
  failures, and timeout on long operations.
  Keywords: API error, 401, 403, 404, 417, 429, 500, CSRF, CORS, REST,, API call fails, 403 forbidden, CORS error, token expired, endpoint not found, webhook not received.
  whitelist, webhook, rate limit, file upload, authentication token.
license: MIT
compatibility: "Claude Code, Claude.ai Projects, Claude API. Frappe v14-v16."
metadata:
  author: OpenAEC-Foundation
  version: "2.0"

API Error Handling

For API implementation patterns see `frappe-core-api`. For permission errors see `frappe-errors-permissions`.

---

HTTP Status Code Map: Error -> Cause -> Fix

| Code | Frappe Exception | When It Happens | Fix | |------|-----------------|-----------------|-----| | 200 | — | Success | — | | 401 | `AuthenticationError` | Bad/expired token, wrong format | Check `Authorization: token key:secret` or `Bearer access_token` | | 403 | `PermissionError` | Missing `@whitelist`, no role, no `allow_guest` | Add decorator or grant permission | | 404 | `DoesNotExistError` | Wrong URL, doc not found, typo in endpoint path | Verify `/api/resource/:doctype/:name` or `/api/method/dotted.path` | | 409 | `DuplicateEntryError` | Unique constraint violated | Check existing records before insert | | 417 | `ValidationError` | `frappe.throw()` called | Fix validation logic or input data | | 429 | `RateLimitExceededError` | Too many requests | Respect `Retry-After` header; throttle requests | | 500 | `Exception` (unhandled) | Unhandled server error | Check Error Log; wrap in try/except | | 503 | — | Server overloaded / maintenance | Retry with exponential backoff |

---

Authentication Errors (401)

Wrong Token Format

Error:  HTTP 401 Unauthorized
Cause:  Using "Bearer api_key:api_secret" instead of "token api_key:api_secret"

**Frappe uses TWO authentication formats — NEVER mix them:**

| Method | Header Format | When to Use | |--------|--------------|-------------| | API Key/Secret | `Authorization: token api_key:api_secret` | Server-to-server, scripts | | OAuth Bearer | `Authorization: Bearer access_token` | OAuth 2.0 flows | | Session Cookie | Cookie from `/api/method/login` | Browser-based apps |

# WRONG — Bearer with API key:secret
headers = {"Authorization": f"Bearer {api_key}:{api_secret}"}

# CORRECT — token keyword for API key:secret
headers = {"Authorization": f"token {api_key}:{api_secret}"}

# CORRECT — Bearer for OAuth access tokens only
headers = {"Authorization": f"Bearer {oauth_access_token}"}

Expired OAuth Token

Error:  HTTP 401 after token was working
Cause:  OAuth access_token expired
Fix:    Use refresh_token to get new access_token
def get_fresh_token(settings):
    """ALWAYS implement token refresh for OAuth integrations."""
    if is_token_expired(settings.token_expiry):
        response = requests.post(f"{settings.base_url}/api/method/frappe.integrations.oauth2.get_token", data={
            "grant_type": "refresh_token",
            "refresh_token": settings.get_password("refresh_token"),
            "client_id": settings.client_id,
        })
        if response.status_code == 200:
            data = response.json()
            settings.access_token = data["access_token"]
            settings.token_expiry = frappe.utils.add_to_date(None, seconds=data["expires_in"])
            settings.save(ignore_permissions=True)
        else:
            frappe.throw(_("OAuth token refresh failed"), exc=frappe.AuthenticationError)
    return settings.access_token

---

Forbidden Errors (403)

Missing @frappe.whitelist()

Error:  HTTP 403 on /api/method/myapp.api.my_function
Cause:  Function exists but lacks @frappe.whitelist() decorator
Fix:    Add decorator — without it, NO external call is allowed
# WRONG — Callable internally but returns 403 via REST
def my_function(name):
    return frappe.get_doc("Item", name)

# CORRECT — Exposed to authenticated users
@frappe.whitelist()
def my_function(name):
    return frappe.get_doc("Item", name)

# CORRECT — Exposed to everyone including unauthenticated
@frappe.whitelist(allow_guest=True)
def public_function():
    return {"status": "ok"}

Missing allow_guest for Public Endpoints

Error:  HTTP 403 for unauthenticated requests
Cause:  @frappe.whitelist() without allow_guest=True
Fix:    Add allow_guest=True — but ALWAYS validate inputs

**NEVER use `allow_guest=True` without input validation** — these endpoints are exposed to the internet.

---

Not Found Errors (404)

Common URL Mistakes

| Wrong URL | Correct URL | Issue | |-----------|-------------|-------| | `/api/resource/SalesOrder/SO-001` | `/api/resource/Sales Order/SO-001` | Space in DocType name | | `/api/method/myapp.my_function` | `/api/method/myapp.api.my_function` | Missing module path | | `/api/resource/sales_order` | `/api/resource/Sales Order` | Wrong case / underscore | | `/api/v2/document/Item/ITEM-001` [v14] | `/api/resource/Item/ITEM-001` | v2 API only in v15+ |

# ALWAYS URL-encode DocType names with spaces
import urllib.parse
url = f"/api/resource/{urllib.parse.quote('Sales Order')}/{name}"

---

Validation Errors (417)

Every `frappe.throw()` call returns HTTP 417 by default (unless a specific exception class is provided).

# Returns 417 — generic validation error
frappe.throw(_("Amount must be positive"))

# Returns 417 — wit
Read more
Ships withfrappe-claude-skill-package

60 deterministic Claude AI skills for Frappe Framework & ERPNext v14-v16 development and operations

Get the whole plugin
Stats
178
Stars
53
Forks
Maintained
Maintenance
Python
Language
2mo ago
Last commit
8mo ago
Created
15d ago
Added

Repo: Impertio-Studio/Frappe_Claude_Skill_Package