frappe-errors-api について
Frappe APIのエラーに対処する開発者向けのトラブルシューティング用リファレンススキルです。HTTPステータスコードと主な原因を対応付け、認証、権限、リクエスト、Webhook、クライアント側のエラー処理を扱います。
主な機能
- 01HTTPステータスコードと例外の対応表
- 02認証とCSRFのトラブルシューティング
- 03検証エラーと権限エラーへの対処
- 04レート制限とタイムアウトの処理パターン
- 05Webhookとファイルアップロードの診断
活用シーン
- 01認証または権限エラーの原因を特定
- 02APIのレート制限や長時間処理に対処
- 03Webhook配信とファイルアップロードの問題を調査
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ impertio-studio/ frappe_claude_skill_package --skill frappe-errors-apiインストール後、Claude Code で/frappe-errors-apiを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
references/anti-patterns.mdexamples.mdpatterns.md詳細
API エラー処理
API の実装パターンについては frappe-core-api を参照してください。権限エラーについては frappe-errors-permissions を参照してください。
HTTP ステータスコード一覧:エラー -> 原因 -> 対処法
| コード | Frappe の例外 | 発生する状況 | 対処法 |
|---|---|---|---|
| 200 | — | 成功 | — |
| 401 | AuthenticationError | トークンが無効または期限切れ、形式が正しくない | Authorization: token key:secret または Bearer access_token を確認します |
| 403 | PermissionError | @whitelist がない、ロールがない、allow_guest がない | デコレーターを追加するか、権限を付与します |
| 404 | DoesNotExistError | URL が正しくない、ドキュメントが見つからない、エンドポイントパスに誤字がある | /api/resource/:doctype/:name または /api/method/dotted.path を確認します |
| 409 | DuplicateEntryError | 一意制約に違反している | 挿入前に既存のレコードを確認します |
| 417 | ValidationError | frappe.throw() が呼び出された | 検証ロジックまたは入力データを修正します |
| 429 | RateLimitExceededError | リクエストが多すぎる | Retry-After ヘッダーに従い、リクエストを抑制します |
| 500 | Exception(未処理) | 未処理のサーバーエラー | Error Log を確認し、try/except で囲みます |
| 503 | — | サーバーが過負荷またはメンテナンス中 | 指数バックオフで再試行します |
認証エラー(401)
トークン形式が正しくない
Error: HTTP 401 Unauthorized
Cause: Using "Bearer api_key:api_secret" instead of "token api_key:api_secret"
Frappe は認証形式を 2 種類使用します。絶対に混在させないでください。
| 方法 | ヘッダー形式 | 使用する状況 |
|---|---|---|
| API Key/Secret | Authorization: token api_key:api_secret | サーバー間通信、スクリプト |
| OAuth Bearer | Authorization: Bearer access_token | OAuth 2.0 フロー |
| セッション Cookie | /api/method/login から取得した Cookie | ブラウザベースのアプリ |
# 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}"}
OAuth トークンの期限切れ
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
アクセス拒否エラー(403)
@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"}
公開エンドポイントに allow_guest がない
Error: HTTP 403 for unauthenticated requests
Cause: @frappe.whitelist() without allow_guest=True
Fix: Add allow_guest=True — but ALWAYS validate inputs
入力を検証せずに allow_guest=True を使用しないでください。これらのエンドポイントはインターネットに公開されます。
未検出エラー(404)
よくある URL の間違い
| 誤った URL | 正しい URL | 問題 |
|---|---|---|
/api/resource/SalesOrder/SO-001 | /api/resource/Sales Order/SO-001 | DocType 名にスペースが必要 |
/api/method/myapp.my_function | /api/method/myapp.api.my_function | モジュールパスがない |
/api/resource/sales_order | /api/resource/Sales Order | 大文字・小文字またはアンダースコアが誤っている |
/api/v2/document/Item/ITEM-001 [v14] | /api/resource/Item/ITEM-001 | v2 API は v15 以降でのみ使用可能 |
# ALWAYS URL-encode DocType names with spaces
import urllib.parse
url = f"/api/resource/{urllib.parse.quote('Sales Order')}/{name}"
検証エラー(417)
frappe.throw() を呼び出すと、特定の例外クラスが指定されていない限り、デフォルトで HTTP 417 が返されます。
# Returns 417 — generic validation error
frappe.throw(_("Amount must be positive"))
# Returns 417 — with explicit ValidationError type
frappe.throw(_("Amount must be positive"), exc=frappe.ValidationError)
# Returns 403 — PermissionError overrides to 403
frappe.throw(_("Access denied"), exc=frappe.PermissionError)
# Returns 404 — DoesNotExistError overrides to 404
frappe.throw(_("Not found"), exc=frappe.DoesNotExistError)
クライアントがエラーの種類を正しく処理できるよう、必ず特定の例外クラスを使用してください。
# WRONG — all errors look the same to the client
frappe.throw(_("Customer not found")) # 417, generic
# CORRECT — client can distinguish 404 from validation error
frappe.throw(_("Customer not found"), exc=frappe.DoesNotExistError) # 404
CSRF トークンエラー
Error: HTTP 403 "CSRF token missing or invalid"
Cause: POST/PUT/DELETE request without X-Frappe-CSRF-Token header
ルール:
- セッションベース(Cookie)の認証では、必ず
X-Frappe-CSRF-Tokenヘッダーを含めてください。 - トークンベースの認証(
Authorization: token ...)では、CSRF トークンは不要です。 - OAuth Bearer 認証では、CSRF トークンは不要です。
- CSRF トークンは JavaScript の
frappe.csrf_tokenで利用できるほか、window.CSRF_TOKENとして埋め込まれています。
// Browser-side: ALWAYS include CSRF for session-based requests
fetch("/api/method/myapp.api.update", {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-Frappe-CSRF-Token": frappe.csrf_token
},
body: JSON.stringify({data: "value"})
});
CORS エラー
Error: "Access-Control-Allow-Origin" header missing
Cause: Cross-origin request not configured in site_config.json
// site_config.json — NEVER use "*" in production
{
"allow_cors": "https://your-frontend.example.com"
}
複数のオリジンを指定する場合[v15+]:
{
"allow_cors": ["https://app1.example.com", "https://app2.example.com"]
}
レート制限エラー(429)
Error: HTTP 429 Too Many Requests
Cause: Exceeded rate limit configured in site_config.json or hooks.py
# hooks.py — rate limiting on whitelisted methods [v14+]
rate_limit = {"myapp.api.heavy_endpoint": {"limit": 10, "seconds": 60}}
外部 API 呼び出しでは、必ず 429 を処理してください。
def call_with_rate_limit(url, data):
response = requests.post(url, json=data, timeout=30)
if response.status_code == 429:
wait = int(response.headers.get("Retry-After", 60))
time.sleep(min(wait, 120)) # Cap at 2 minutes
response = requests.post(url, json=data, timeout=30)
response.raise_for_status()
return response.json()
ファイルアップロードエラー
Error: HTTP 500 on /api/method/upload_file
Cause: Wrong content type, file too large, or missing file field
# CORRECT file upload via REST API
import requests
response = requests.post(
f"{base_url}/api/method/upload_file",
headers={"Authorization": f"token {api_key}:{api_secret}"},
files={"file": ("document.pdf", open("document.pdf", "rb"), "application/pdf")},
data={
"doctype": "Sales Invoice",
"docname": "SINV-001",
"is_private": 1 # 1 = private, 0 = public
},
timeout=60 # ALWAYS set timeout for uploads
)
よくあるアップロードの失敗:
Content-Typeはmultipart/form-dataにする必要があります(files=パラメーターを設定すると自動的に設定されます)- ファイルのアップロードに
Content-Type: application/jsonを設定しないでください - site_config.json の
max_file_sizeを確認してください(デフォルトは 10MB) - [v15+]
allowed_file_extensionsにより、ファイル形式が制限されます
JSON 解析エラー
Error: "Failed to decode JSON" or unexpected behavior
Cause: API arguments sent as JSON string instead of parsed object
@frappe.whitelist()
def update_items(items):
# ALWAYS handle both string and parsed input
if isinstance(items, str):
try:
items = frappe.parse_json(items)
except Exception:
frappe.throw(_("Invalid JSON format"), exc=frappe.ValidationError)
if not isinstance(items, (list, dict)):
frappe.throw(_("Expected list or dict"), exc=frappe.ValidationError)
Webhook 配信エラー
Error: Webhook not firing or returning errors
Cause: Target URL unreachable, wrong format, or timeout
デバッグ用チェックリスト:
- Error Log で Webhook 配信エラーを確認します
- サーバーから対象の URL にアクセスできることを確認します
- Webhook の条件を確認します。イベントがフィルタリングされていませんか?
- [v15+]Webhook Request Log で配信状況を確認します
# Custom webhook with error handling
@frappe.whitelist(allow_guest=True)
def incoming_webhook():
"""Handle incoming webhook with validation."""
payload = frappe.request.data
signature = frappe.request.headers.get("X-Webhook-Signature")
if not verify_signature(payload, signature):
frappe.local.response["http_status_code"] = 401
return {"error": "Invalid signature"}
try:
data = frappe.parse_json(payload)
except Exception:
frappe.local.response["http_status_code"] = 400
return {"error": "Invalid JSON payload"}
# ALWAYS return 200 quickly to prevent sender retries
frappe.enqueue(process_webhook_data, data=data, queue="short")
return {"status": "accepted"}
長時間実行される操作のタイムアウト
Error: HTTP 504 Gateway Timeout or connection reset
Cause: Operation takes longer than proxy/server timeout (typically 60s)
修正方法:長時間実行される操作にはバックグラウンドジョブを使用してください。
@frappe.whitelist()
def start_long_operation(filters):
"""NEVER run long operations synchronously in API calls."""
job_id = frappe.generate_hash(length=10)
frappe.enqueue(
"myapp.tasks.run_long_operation",
queue="long",
timeout=600,
job_id=job_id,
filters=filters
)
return {"status": "queued", "job_id": job_id}
@frappe.whitelist()
def check_job_status(job_id):
"""Poll for job completion."""
from frappe.utils.background_jobs import get_info
jobs = get_info()
for job in jobs:
if job.get("job_id") == job_id:
return {"status": job.get("status", "unknown")}
return {"status": "completed"}
サーバー側のエラーパターン(標準)
@frappe.whitelist()
def safe_api_endpoint(docname, action):
"""ALWAYS follow: validate -> check permission -> execute -> handle errors."""
# 1. Validate input
if not docname:
frappe.throw(_("Document name required"), exc=frappe.ValidationError)
# 2. Check existence
if not frappe.db.exists("My DocType", docname):
frappe.throw(_("Document not found"), exc=frappe.DoesNotExistError)
# 3. Check permission
frappe.has_permission("My DocType", "write", docname, throw=True)
# 4. Execute with error handling
try:
doc = frappe.get_doc("My DocType", docname)
result = doc.run_method(action)
return {"status": "success", "data": result}
except frappe.ValidationError:
raise # Let Frappe handle — returns 417
except frappe.PermissionError:
raise # Let Frappe handle — returns 403
except Exception:
frappe.log_error(frappe.get_traceback(), f"API Error: {docname}")
frappe.throw(_("Operation failed. Please try again."))
クライアント側のエラー処理
// ALWAYS handle errors in frappe.call
frappe.call({
method: "myapp.api.safe_api_endpoint",
args: {docname: "DOC-001", action: "approve"},
freeze: true,
freeze_message: __("Processing..."),
callback: function(r) {
if (r.message && r.message.status === "success") {
frappe.show_alert({message: __("Done"), indicator: "green"});
}
},
error: function(r) {
// ALWAYS check exc_type for specific handling
if (r.exc_type === "PermissionError") {
frappe.msgprint(__("You lack permission for this action."));
} else if (r.exc_type === "DoesNotExistError") {
frappe.msgprint(__("Record not found."));
} else if (!r.status) {
frappe.msgprint(__("Network error. Check your connection."));
}
}
});
重要なルール
必ず行うこと
frappe.throw()では具体的な例外クラスを使用してください — 適切な HTTP ステータスコードが設定されます- すべての外部リクエストにタイムアウトを設定してください —
requests.get(url, timeout=30) - 処理の前にすべての入力を検証してください — ホワイトリスト登録されたメソッドは、ログイン済みのすべてのユーザーが呼び出せます
- エラーをスローする前にログに記録してください —
frappe.log_error()の後にfrappe.throw()を呼び出します - すべての
frappe.call()でエラーコールバックを処理してください — 失敗が通知されないとユーザーが混乱します - 30 秒を超える操作にはバックグラウンドジョブを使用してください
- 受信した Webhook には速やかに 200 を返し、その後非同期で処理してください
絶対に行わないこと
- 内部エラーをユーザーに公開しないでください — トレースバックをログに記録し、わかりやすいメッセージを表示します
- トークン形式を混在させないでください —
token key:secretとBearer oauth_token - 4xx エラー(429 を除く)を再試行しないでください — 一時的な障害ではなく、クライアントの不具合を示しています
- セッションベースの POST リクエストで CSRF トークンを省略しないでください — 403 が発生します
- ファイルのアップロードで Content-Type: application/json を設定しないでください — multipart/form-data にする必要があります
- ログを記録せずに例外をキャッチしないでください — 本番環境でのデバッグが不可能になります
- API 認証情報をハードコードしないでください — DocType の
settings.get_password("field")を使用します
参考ファイル
| ファイル | 内容 |
|---|---|
references/patterns.md | ホワイトリスト登録済みメソッド、Webhook、外部 API の完全なパターン |
references/examples.md | 動作する API モジュール、クライアント統合、外部 API クライアントの完全な例 |
references/anti-patterns.md | API のエラー処理でよくある 15 の間違い |
関連項目
frappe-core-api— API の実装パターンfrappe-errors-permissions— 権限エラーの処理(403 の詳細解説)frappe-syntax-whitelisted— ホワイトリスト登録済みメソッドの構文frappe-errors-serverscripts— Server Script のエラー処理
コメント
Markdown 対応