Skip to content
AI & Agents
Skill

/frappe-errors-permissions

Use when debugging or handling permission errors in Frappe/ERPNext. Prevents broken document access from throwing in permission hooks. Covers PermissionError (403), has_permission hook failures, User Permission restricting too much or too little, perm_level blocking field

From plugin
frappe-claude-skill-package
17861 skills
Install
$ npx -y skills add Impertio-Studio/Frappe_Claude_Skill_Package --skill frappe-errors-permissions --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-permissions

Context preview

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

Use when debugging or handling permission errors in Frappe/ERPNext. Prevents broken document access from throwing in permission hooks. Covers PermissionError (403), has_permission hook failures, User Permission restricting too much or too little, perm_level blocking field

SKILL.md

frappe-errors-permissions.SKILL.md
name: frappe-errors-permissions
description: >
  Use when debugging or handling permission errors in Frappe/ERPNext.
  Prevents broken document access from throwing in permission hooks.
  Covers PermissionError (403), has_permission hook failures, User Permission
  restricting too much or too little, perm_level blocking field access,
  System Manager bypass not working, Guest access denied, sharing permissions
  not applying, permission_query_conditions breaking get_list, owner-based
  permissions confusion, Apply User Permission checkbox behavior, and the
  permission debug workflow using frappe.permissions.get_doc_permissions.
  Keywords: PermissionError, has_permission, permission_query_conditions,, permission denied, cannot access, user blocked, sharing not working, role not enough.
  User Permission, perm_level, sharing, guest access, owner permission.
license: MIT
compatibility: "Claude Code, Claude.ai Projects, Claude API. Frappe v14-v16."
metadata:
  author: OpenAEC-Foundation
  version: "2.0"

Permission Error Handling

For permission system overview see `frappe-core-permissions`. For hook syntax see `frappe-syntax-hooks`.

---

Quick Diagnostic: Error Message -> Cause -> Fix

| Error Message | Cause | Fix | |---------------|-------|-----| | `frappe.exceptions.PermissionError` | User lacks role or doc-level access | Add role in Role Permissions Manager or grant User Permission | | "Not permitted" on document open | `has_permission` hook returns False or role missing read | Check `frappe.permissions.get_doc_permissions(doc, user)` output | | List view shows 0 records | `permission_query_conditions` returns overly restrictive SQL | Debug the SQL condition; check User Permissions for the Link field | | "Not allowed to access ... for Guest" | Endpoint missing `allow_guest=True` or DocType lacks Guest read | Add `allow_guest=True` to `@frappe.whitelist()` | | Field invisible despite role having read | `perm_level` > 0 on field and role lacks that level | Add role permission row for the specific `perm_level` | | "User Permission restriction" blocking | User Permission on a Link field auto-filters documents | Uncheck "Apply User Permissions" on that role row or add matching User Permission | | Sharing not granting access | Sharing adds access but never overrides role absence | User MUST have base role permission; sharing only adds doc-level grants | | `ignore_permissions` has no effect | Flag set after `get_doc` already checked permissions | Set `flags.ignore_permissions = True` BEFORE calling `save()` or `insert()` | | System Manager cannot access | Custom `has_permission` hook denies without checking role | ALWAYS check for System Manager / Administrator in hook |

---

Decision Tree: Where Is the Error?

Permission error occurred
├── Document-level (single doc access)?
│   ├── has_permission hook returning False?
│   │   └── Debug: frappe.permissions.get_doc_permissions(doc, user)
│   ├── User Permission restricting Link field?
│   │   └── Check: frappe.get_all("User Permission", filters={"user": user})
│   ├── perm_level blocking field?
│   │   └── Check: role has permission row for that perm_level
│   └── Sharing not applying?
│       └── Check: user has base role + sharing record exists
├── List-level (0 records in list view)?
│   ├── permission_query_conditions returning bad SQL?
│   │   └── Debug: run condition manually in MariaDB console
│   ├── User Permission auto-filtering?
│   │   └── Check "Apply User Permissions" checkbox on role row
│   └── get_all vs get_list confusion?
│       └── ALWAYS use get_list for user-facing queries
├── API endpoint (403 response)?
│   ├── Missing @frappe.whitelist()?
│   │   └── Add decorator to Python method
│   ├── Missing allow_guest=True?
│   │   └── Add allow_guest parameter for public endpoints
│   └── frappe.only_for() blocking?
│       └── Check user has required role
└── System Manager bypass failing?
    └── Custom hook does not check for System Manager role

---

Permission Hook Errors

has_permission Hook: NEVER Throw

# hooks.py
has_permission = {
    "Sales Order": "myapp.permissions.sales_order_has_permission",
}
# WRONG — Breaks ALL document access
def sales_order_has_permission(doc, user, permission_type):
    if doc.status == "Locked":
        frappe.throw("Locked")  # NEVER do this

# CORRECT — Return False to deny, None to defer
def sales_order_has_permission(doc, user, permission_type):
    """
    ALWAYS wrap in try/except. NEVER throw. NEVER return True.
    Returns: False (deny) or None (defer to standard system).
    """
    try:
        user = user or frappe.session.user
        if user == "Administrator":
            return None

        # ALWAYS check System Manager early
        if "System Manager" in frappe.get_roles(user):
            return None

        # Deny write on locked docs (but allow read)
        if permission_type in ("write", "delete", "cancel"):
            if doc.get("status") == "Locked":
                return False

        return None  # Defer to standard permission system

    except Exception:
        frappe.log_error(frappe.get_traceback(),
            f"has_permission error: {getattr(doc, 'name', 'unknown')}")
        return None  # Safe fallback — defer

**Critical rules for has_permission hooks:**

  • ALWAYS return `None` to defer, `False` to deny. NEVER return `True` — hooks can only restrict, not grant.
  • ALWAYS wrap the entire function in `try/except`. An unhandled exception breaks ALL access to that DocType.
  • ALWAYS check for `Administrator` and `System Manager` at the top.
  • NEVER call `frappe.throw()` inside this hook.

permission_query_conditions: NEVER Throw

# hooks.py
permission_query_conditions = {
    "Sales Order": "myapp.permissions.sales_order_query",
}
# WRONG — Breaks list view for all users
def sales_order_query(user):
    if not user:
        frappe.throw("User required")  # NEVER d
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
14d ago
Added

Repo: Impertio-Studio/Frappe_Claude_Skill_Package