mcpField
F
frappe-errors-serverscripts

frappe-errors-serverscripts

github.com/impertio-studio開発198109 回インストール30 週間前に更新
リポジトリ

frappe-errors-serverscripts について

Frappe の Server Script を扱う開発者向けのトラブルシューティングガイドです。よくあるエラーの原因と対処法に加え、サンドボックスの制約やスクリプト種別ごとの動作を説明します。

主な機能

  1. 01エラー別の原因と対処法
  2. 02RestrictedPython の制約説明
  3. 03スクリプト種別ごとのコンテキスト比較
  4. 04スケジューラーと API の注意点
  5. 05SQL インジェクション対策

活用シーン

  1. 01Server Script の import や名前エラーの診断
  2. 02スケジューラーのコミットやタイムアウト問題の修正
  3. 03API の空レスポンスの原因調査
  4. 04SQL クエリにおけるユーザー入力の確認

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/impertio-studio/frappe_claude_skill_package --skill frappe-errors-serverscripts

インストール後、Claude Code で/frappe-errors-serverscriptsを入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

references/
anti-patterns.md
examples.md
patterns.md

詳細

サーバースクリプトのエラー — 診断と解決

関連項目:frappe-syntax-serverscripts(構文)、frappe-impl-serverscripts(ワークフロー)、frappe-errors-clientscripts(クライアント側)。


重要:サーバースクリプトはデフォルトで無効です[v15+]

Frappe v15 以降、サーバースクリプトはデフォルトで無効です。有効にする必要があります。

# In site_config.json
{ "server_script_enabled": 1 }

Frappe Cloud では、サーバースクリプトはプライベートベンチでのみ利用でき、共有ベンチでは利用できません。


エラー診断フローチャート

ERROR IN SERVER SCRIPT
│
├─► ImportError / NameError
│   ├─► "import json" → BLOCKED. Use frappe.parse_json()
│   ├─► "import datetime" → BLOCKED. Use frappe.utils
│   ├─► "import os/sys/subprocess" → BLOCKED. Security restriction
│   └─► "NameError: name 'dict' is not defined" → Some builtins restricted
│
├─► SyntaxError: not allowed
│   ├─► "try/except" → BLOCKED by RestrictedPython [v14-v15]
│   ├─► "raise ValueError" → BLOCKED. Use frappe.throw()
│   └─► "exec/eval" → BLOCKED. Security restriction
│
├─► Script runs but nothing happens
│   ├─► Wrong Script Type selected → Check Document Event vs API vs Scheduler
│   ├─► Wrong DocType selected → Verify exact DocType name
│   ├─► Wrong Event selected → Before Save ≠ After Save
│   └─► Script disabled → Check "Enabled" checkbox
│
├─► 403 Permission Denied
│   ├─► Scheduler script → Runs as Administrator, check role permissions
│   ├─► API script → Check Allow Guest setting
│   └─► doc_event → User lacks DocType permission
│
├─► Data not saved in Scheduler
│   └─► Missing frappe.db.commit() → REQUIRED in scheduler scripts
│
└─► API script returns empty/wrong response
    └─► Not setting frappe.response["message"] → ALWAYS set response

エラーメッセージ → 原因 → 修正方法の表

エラーメッセージ原因修正方法
ImportError: import not allowedサンドボックス内での import 文frappe.utils、frappe.parse_json() などを使用します
NameError: name 'dict' is not definedRestrictedPython によって一部の Python 組み込み関数がブロックされていますfrappe._dict() またはリテラル {} を使用します
SyntaxError: try/except not allowedRestrictedPython によって例外処理がブロックされています[v14-v15]条件チェック(if/else)を代わりに使用します
SyntaxError: raise not allowedRestrictedPython によって raise がブロックされていますfrappe.throw() を使用します
Script not executingScript Type または Event の選択が間違っていますDocument Event、API、Scheduler のいずれかに一致することを確認します
doc is not definedAPI または Scheduler スクリプトで doc を使用しています(ドキュメントコンテキストがありません)doc は Document Event スクリプトでのみ利用できます
Scheduler での PermissionErrorScheduler は Administrator として実行されますが、スクリプトが制限されたリソースにアクセスしています適切な場合は ignore_permissions=True を使用します
Scheduler での Changes not savedfrappe.db.commit() がありませんScheduler スクリプトでは必ず frappe.db.commit() を呼び出してください
API returns empty responsefrappe.response["message"] の設定を忘れています必ず frappe.response["message"] = result を設定してください
Timeout / killed無限ループ、または処理するレコードが多すぎますクエリには必ず limit を追加し、必ずバッチ処理を使用してください
ValidationError: qty is requiredBefore Save で doc.save() が呼び出されています(再帰)Before Save で doc.save() を呼び出さないでください。値を設定するだけにします
SQL injection via string formatエスケープされていない SQL 内のユーザー入力必ず frappe.db.escape() またはパラメーター化クエリを使用します

最も多いエラー:ImportError

**初心者は誰もが一度は遭遇します。**サーバースクリプトのサンドボックスでは、json 以外のすべてのインポートがブロックされます。

# ❌ BLOCKED — These ALL fail with ImportError
import json                    # Use frappe.parse_json() / frappe.as_json()
from datetime import datetime  # Use frappe.utils.now(), frappe.utils.today()
import re                      # Not available in sandbox
import os                      # Security: blocked
import requests                # Use frappe.make_get_request(), frappe.make_post_request()

# ✅ CORRECT — Sandbox equivalents
data = frappe.parse_json(doc.json_field)         # Instead of json.loads()
today = frappe.utils.today()                      # Instead of datetime.date.today()
now = frappe.utils.now()                          # Instead of datetime.now()
diff = frappe.utils.date_diff(date1, date2)       # Instead of timedelta
resp = frappe.make_get_request("https://api.com") # Instead of requests.get()
resp = frappe.make_post_request("https://api.com", data=payload)
利用可能なサンドボックス API(完全なリファレンス)
カテゴリー利用可能なメソッド
ドキュメントfrappe.get_doc()、frappe.new_doc()、frappe.get_last_doc()、frappe.get_cached_doc()、frappe.get_mapped_doc()、frappe.rename_doc()、frappe.delete_doc()
データベースfrappe.db.get_list()、frappe.db.get_all()、frappe.db.get_value()、frappe.db.get_single_value()、frappe.db.set_value()、frappe.db.exists()、frappe.db.sql()、frappe.db.commit()、frappe.db.rollback()、frappe.db.escape()
クエリビルダーfrappe.qb(完全なクエリビルダー)
HTTPfrappe.make_get_request()、frappe.make_post_request()、frappe.make_put_request()
ユーティリティfrappe.utils.*(すべてのユーティリティ関数)、frappe.parse_json()、frappe.as_json()
ユーザー/セッションfrappe.session.user、frappe.get_roles()、frappe.has_permission()
メッセージfrappe.throw()、frappe.msgprint()、frappe.log_error()、frappe.sendmail()
モジュールjson(インポート可能な唯一のモジュール)

スクリプトタイプの選択ミス

正しい Script Type を選択したことを必ず確認してください。

Script Typeトリガーdoc あり?frappe.form_dict あり?自動コミット?
Document EventDocType のライフサイクル(Before Save、After Save など)YESNOYES
API/api/method/{method_name} への HTTP リクエストNOYESYES
Scheduler EventCron スケジュールNONONO — frappe.db.commit() の呼び出しが必須
Permission QueryDocType に対するすべてのリストクエリNONO(user あり)該当なし
よくある間違い:イベントの指定ミス
# ❌ WRONG — "After Save" cannot prevent save
# Script Type: Document Event, Event: After Save
if not doc.customer:
    frappe.throw("Customer is required")  # Document already saved!

# ✅ CORRECT — Use "Before Save" or "Before Validate"
# Script Type: Document Event, Event: Before Save
if not doc.customer:
    frappe.throw("Customer is required")  # Prevents save

サンドボックスでの回避策

try/except はブロックされています:条件チェックを使用する
# ❌ BLOCKED in sandbox
try:
    customer = frappe.get_doc("Customer", doc.customer)
except Exception:
    frappe.throw("Customer not found")

# ✅ CORRECT — Check first, then access
if not frappe.db.exists("Customer", doc.customer):
    frappe.throw(f"Customer '{doc.customer}' not found")
customer = frappe.get_doc("Customer", doc.customer)
raise はブロックされています:frappe.throw() を使用する
# ❌ BLOCKED
if amount < 0:
    raise ValueError("Amount cannot be negative")

# ✅ CORRECT
if amount < 0:
    frappe.throw("Amount cannot be negative")
API スクリプトでの frappe.throw() の例外タイプ
例外HTTP コード使用する場面
frappe.ValidationError417入力検証の失敗
frappe.PermissionError403アクセス拒否
frappe.DoesNotExistError404レコードが見つからない
frappe.AuthenticationError401ログインしていない
(デフォルト、exc なし)417一般的な検証エラー
# API Script — Correct exception types
if not customer:
    frappe.throw("Customer param required", exc=frappe.ValidationError)  # 417
if not frappe.db.exists("Customer", customer):
    frappe.throw("Customer not found", exc=frappe.DoesNotExistError)    # 404
if not frappe.has_permission("Customer", "read", customer):
    frappe.throw("Access denied", exc=frappe.PermissionError)           # 403

スケジューラースクリプト:重大な間違い

# ❌ WRONG — No limit, no commit, no error logging
invoices = frappe.get_all("Sales Invoice", filters={"status": "Unpaid"})
for inv in invoices:
    frappe.db.set_value("Sales Invoice", inv.name, "reminder_sent", 1)

# ✅ CORRECT — Limit, batch commit, error logging
BATCH_SIZE = 50
invoices = frappe.get_all(
    "Sales Invoice",
    filters={"status": "Unpaid", "docstatus": 1},
    fields=["name", "customer"],
    limit=500  # ALWAYS limit
)

errors = []
for i in range(0, len(invoices), BATCH_SIZE):
    batch = invoices[i:i + BATCH_SIZE]
    for inv in batch:
        if not frappe.db.exists("Customer", inv.customer):
            errors.append(f"{inv.name}: Customer not found")
            continue
        frappe.db.set_value("Sales Invoice", inv.name, "reminder_sent", 1)
    frappe.db.commit()  # REQUIRED

if errors:
    frappe.log_error("\n".join(errors), "Reminder Errors")
frappe.db.commit()

SQL インジェクションの防止

# ❌ VULNERABLE — String interpolation with user input
territory = frappe.form_dict.get("territory")
conditions = f"`tabCustomer`.territory = '{territory}'"  # SQL INJECTION!

# ✅ SAFE — Use frappe.db.escape()
territory = frappe.form_dict.get("territory")
conditions = f"`tabCustomer`.territory = {frappe.db.escape(territory)}"

# ✅ SAFEST — Use parameterized query or Query Builder
results = frappe.db.get_all("Customer", filters={"territory": territory})

必ず守ること/絶対にしないこと

必ず守ること
  1. Python のインポートの代わりに frappe.utils.* を使用する — インポートできるのは json モジュールのみです
  2. raise の代わりに frappe.throw() を使用する — raise はサンドボックスでブロックされています
  3. try/except の代わりに条件チェックを使用する — 例外処理はブロックされています [v14-v15]
  4. スケジューラースクリプトでは frappe.db.commit() を呼び出す — 変更は自動コミットされません
  5. スケジューラースクリプトのすべてのクエリに limit を追加する — メモリの枯渇を防ぎます
  6. API スクリプトで frappe.response["message"] を設定する — そうしないとレスポンスは空になります
  7. SQL でユーザー入力に frappe.db.escape() を使用する — SQL インジェクションを防ぎます
  8. スケジューラースクリプトのエラーを frappe.log_error() でログに記録する — エラーを確認するユーザーがいません
  9. スクリプトタイプが目的に合っていることを確認する — ドキュメントイベント、API、スケジューラー
絶対にしないこと
  1. import 文を絶対に使用しない(json を除く)— RestrictedPython によってブロックされています
  2. try/except または raise を絶対に使用しない — サンドボックスでブロックされています [v14-v15]
  3. Before Save で doc.save() を絶対に呼び出さない — 無限再帰が発生します
  4. ユーザー入力を含む SQL に文字列フォーマットを絶対に使用しない — SQL インジェクションのリスクがあります
  5. スケジューラーでレコードを無制限に処理しない — 必ず limit を使用してください
  6. API/スケジューラースクリプトで doc が存在すると想定しない — ドキュメントイベントでのみ使用できます
  7. スケジューラーで frappe.db.commit() を忘れない — すべての変更が失われます

参照ファイル

ファイル内容
references/examples.md診断付きの実際のエラーシナリオ
references/anti-patterns.md修正方法を含む、よくあるサンドボックスの間違い
references/patterns.mdスクリプトタイプ別の防御的なエラー処理パターン

コメント