$npx -y skills add Impertio-Studio/Frappe_Claude_Skill_Package --skill frappe-errors-serverscriptsUse 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,
| 1 | # Server Script Errors — Diagnosis and Resolution |
| 2 | |
| 3 | Cross-refs: `frappe-syntax-serverscripts` (syntax), `frappe-impl-serverscripts` (workflows), `frappe-errors-clientscripts` (client-side). |
| 4 | |
| 5 | --- |
| 6 | |
| 7 | ## CRITICAL: Server Scripts Disabled by Default [v15+] |
| 8 | |
| 9 | Starting from Frappe v15, Server Scripts are **disabled by default**. You MUST enable them: |
| 10 | |
| 11 | ```python |
| 12 | # In site_config.json |
| 13 | { "server_script_enabled": 1 } |
| 14 | ``` |
| 15 | |
| 16 | On Frappe Cloud: Server Scripts are ONLY available on **private benches**, NOT on shared benches. |
| 17 | |
| 18 | --- |
| 19 | |
| 20 | ## Error Diagnosis Flowchart |
| 21 | |
| 22 | ``` |
| 23 | ERROR IN SERVER SCRIPT |
| 24 | │ |
| 25 | ├─► ImportError / NameError |
| 26 | │ ├─► "import json" → BLOCKED. Use frappe.parse_json() |
| 27 | │ ├─► "import datetime" → BLOCKED. Use frappe.utils |
| 28 | │ ├─► "import os/sys/subprocess" → BLOCKED. Security restriction |
| 29 | │ └─► "NameError: name 'dict' is not defined" → Some builtins restricted |
| 30 | │ |
| 31 | ├─► SyntaxError: not allowed |
| 32 | │ ├─► "try/except" → BLOCKED by RestrictedPython [v14-v15] |
| 33 | │ ├─► "raise ValueError" → BLOCKED. Use frappe.throw() |
| 34 | │ └─► "exec/eval" → BLOCKED. Security restriction |
| 35 | │ |
| 36 | ├─► Script runs but nothing happens |
| 37 | │ ├─► Wrong Script Type selected → Check Document Event vs API vs Scheduler |
| 38 | │ ├─► Wrong DocType selected → Verify exact DocType name |
| 39 | │ ├─► Wrong Event selected → Before Save ≠ After Save |
| 40 | │ └─► Script disabled → Check "Enabled" checkbox |
| 41 | │ |
| 42 | ├─► 403 Permission Denied |
| 43 | │ ├─► Scheduler script → Runs as Administrator, check role permissions |
| 44 | │ ├─► API script → Check Allow Guest setting |
| 45 | │ └─► doc_event → User lacks DocType permission |
| 46 | │ |
| 47 | ├─► Data not saved in Scheduler |
| 48 | │ └─► Missing frappe.db.commit() → REQUIRED in scheduler scripts |
| 49 | │ |
| 50 | └─► API script returns empty/wrong response |
| 51 | └─► Not setting frappe.response["message"] → ALWAYS set response |
| 52 | ``` |
| 53 | |
| 54 | --- |
| 55 | |
| 56 | ## Error Message → Cause → Fix Table |
| 57 | |
| 58 | | Error Message | Cause | Fix | |
| 59 | |---------------|-------|-----| |
| 60 | | `ImportError: import not allowed` | Any `import` statement in sandbox | Use `frappe.utils`, `frappe.parse_json()`, etc. | |
| 61 | | `NameError: name 'dict' is not defined` | Some Python builtins blocked by RestrictedPython | Use `frappe._dict()` or literal `{}` | |
| 62 | | `SyntaxError: try/except not allowed` | RestrictedPython blocks exception handling [v14-v15] | Use conditional checks (`if/else`) instead | |
| 63 | | `SyntaxError: raise not allowed` | RestrictedPython blocks `raise` | Use `frappe.throw()` | |
| 64 | | `Script not executing` | Wrong Script Type or Event selected | Verify type matches: Document Event, API, or Scheduler | |
| 65 | | `doc is not defined` | Using `doc` in API or Scheduler script (no document context) | `doc` is only available in Document Event scripts | |
| 66 | | `PermissionError` in Scheduler | Scheduler runs as Administrator but script accesses restricted resource | Use `ignore_permissions=True` where appropriate | |
| 67 | | `Changes not saved` in Scheduler | Missing `frappe.db.commit()` | ALWAYS call `frappe.db.commit()` in Scheduler scripts | |
| 68 | | `API returns empty response` | Forgot to set `frappe.response["message"]` | ALWAYS set `frappe.response["message"] = result` | |
| 69 | | `Timeout / killed` | Infinite loop or processing too many records | ALWAYS add `limit` to queries, ALWAYS use batch processing | |
| 70 | | `ValidationError: qty is required` | `doc.save()` called in Before Save (recursion) | NEVER call `doc.save()` in Before Save; just set values | |
| 71 | | `SQL injection via string format` | User input in SQL without escaping | ALWAYS use `frappe.db.escape()` or parameterized queries | |
| 72 | |
| 73 | --- |
| 74 | |
| 75 | ## The #1 Error: ImportError |
| 76 | |
| 77 | **Every beginner hits this.** The Server Script sandbox blocks ALL imports except `json`. |
| 78 | |
| 79 | ```python |
| 80 | # ❌ BLOCKED — These ALL fail with ImportError |
| 81 | import json # Use frappe.parse_json() / frappe.as_json() |
| 82 | from datetime import datetime # Use frappe.utils.now(), frappe.utils.today() |
| 83 | import re # Not available in sandbox |
| 84 | import os # Security: blocked |
| 85 | import requests # Use frappe.make_get_request(), frappe.make_post_request() |
| 86 | |
| 87 | # ✅ CORRECT — Sandbox equivalents |
| 88 | data = frappe.parse_json(doc.json_field) # Instead of json.loads() |
| 89 | today = frappe.utils.today() # Instead of datetime.date.today() |
| 90 | now = frappe.utils.now() |