frappe-agent-architect
Use when designing multi-app Frappe architectures, deciding whether to split functionality into separate apps, or implementing…
Use when debugging or preventing errors in Frappe Server Scripts. Prevents ImportError (the #1 error), NameError for restricted builtins, sandbox violations, doc_events not firing, wrong script type selection, SQL injection, permission denied in scheduled scripts, infinite
Claude Code
$ npx -y skills add Impertio-Studio/Frappe_Claude_Skill_Package --skill frappe-errors-serverscripts --agent claude-code
Codex
$ npx skills add Impertio-Studio/Frappe_Claude_Skill_Package --skill frappe-errors-serverscripts -a codex -y
Cursor
$ npx skills add Impertio-Studio/Frappe_Claude_Skill_Package --skill frappe-errors-serverscripts -a cursor -y
Gemini CLI
$ npx skills add Impertio-Studio/Frappe_Claude_Skill_Package --skill frappe-errors-serverscripts -a gemini-cli -y
GitHub Copilot
$ npx skills add Impertio-Studio/Frappe_Claude_Skill_Package --skill frappe-errors-serverscripts -a github-copilot -y
OpenCode
$ npx skills add Impertio-Studio/Frappe_Claude_Skill_Package --skill frappe-errors-serverscripts -a opencode -y
Windsurf
$ npx skills add Impertio-Studio/Frappe_Claude_Skill_Package --skill frappe-errors-serverscripts -a windsurf -y
name: frappe-errors-serverscripts description: > Use when debugging or preventing errors in Frappe Server Scripts. Prevents ImportError (the #1 error), NameError for restricted builtins, sandbox violations, doc_events not firing, wrong script type selection, SQL injection, permission denied in scheduled scripts, infinite loops, and API scripts not returning JSON. Covers error message mapping table. Keywords: server script error, ImportError, NameError, sandbox,, ImportError in server script, script not running, sandbox error, restricted function. restricted, frappe.throw, doc_events, scheduler, API script, SQL injection. license: MIT compatibility: "Claude Code, Claude.ai Projects, Claude API. Frappe v14-v16." metadata: author: OpenAEC-Foundation version: "2.0"
Cross-refs: `frappe-syntax-serverscripts` (syntax), `frappe-impl-serverscripts` (workflows), `frappe-errors-clientscripts` (client-side).
---
Starting from Frappe v15, Server Scripts are **disabled by default**. You MUST enable them:
# In site_config.json
{ "server_script_enabled": 1 }On Frappe Cloud: Server Scripts are ONLY available on **private benches**, NOT on shared benches.
---
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---
| Error Message | Cause | Fix | |---------------|-------|-----| | `ImportError: import not allowed` | Any `import` statement in sandbox | Use `frappe.utils`, `frappe.parse_json()`, etc. | | `NameError: name 'dict' is not defined` | Some Python builtins blocked by RestrictedPython | Use `frappe._dict()` or literal `{}` | | `SyntaxError: try/except not allowed` | RestrictedPython blocks exception handling [v14-v15] | Use conditional checks (`if/else`) instead | | `SyntaxError: raise not allowed` | RestrictedPython blocks `raise` | Use `frappe.throw()` | | `Script not executing` | Wrong Script Type or Event selected | Verify type matches: Document Event, API, or Scheduler | | `doc is not defined` | Using `doc` in API or Scheduler script (no document context) | `doc` is only available in Document Event scripts | | `PermissionError` in Scheduler | Scheduler runs as Administrator but script accesses restricted resource | Use `ignore_permissions=True` where appropriate | | `Changes not saved` in Scheduler | Missing `frappe.db.commit()` | ALWAYS call `frappe.db.commit()` in Scheduler scripts | | `API returns empty response` | Forgot to set `frappe.response["message"]` | ALWAYS set `frappe.response["message"] = result` | | `Timeout / killed` | Infinite loop or processing too many records | ALWAYS add `limit` to queries, ALWAYS use batch processing | | `ValidationError: qty is required` | `doc.save()` called in Before Save (recursion) | NEVER call `doc.save()` in Before Save; just set values | | `SQL injection via string format` | User input in SQL without escaping | ALWAYS use `frappe.db.escape()` or parameterized queries |
---
**Every beginner hits this.** The Server Script sandbox blocks ALL imports except `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)| Category | Available Methods | |----------|-------------------| | **Document** | `frappe.get_doc()`, `frappe.new_doc()`, `frappe.get_last_doc()`, `frappe.get_cached_doc()`, `frappe.get_mapped_doc()`, `frappe.rename_doc()`, `frappe.delete_doc()` | | **Database** | `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()` | | **Query Builder** | `frappe.qb` (full query builder) | | **HTTP** | `frappe.make_get_request()`, `frappe.make_post_request()`, `frappe.make_put_request()` | | **Utility**
60 deterministic Claude AI skills for Frappe Framework & ERPNext v14-v16 development and operations
Use when designing multi-app Frappe architectures, deciding whether to split functionality into separate apps, or implementing…
Use when debugging Frappe errors, using bench console for live inspection, analyzing tracebacks, or reading Frappe log files.…
Use when receiving vague or unclear ERPNext/Frappe development requests that need interpretation. Transforms requirements like…
Use when migrating a Frappe app between major versions, detecting breaking API changes, or resolving post-migration errors.…
Use when reviewing or validating Frappe/ERPNext code against best practices and common pitfalls. Checks generated code before…
Use when building ERPNext/Frappe API integrations (v14/v15/v16) including REST API, RPC API, authentication, webhooks, and rate…
Behavioral guidelines to reduce common LLM coding mistakes. Use when writing, reviewing, or refactoring code to avoid…
Design and build AI agents for any domain. Use when users: (1) ask to "create an agent", "build an assistant", or "design an AI…