erpnext-errors-api

ERPNext API Error Handling

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "erpnext-errors-api" with this command: npx skills add openaec-foundation/erpnext_anthropic_claude_development_skill_package/openaec-foundation-erpnext-anthropic-claude-development-skill-package-erpnext-errors-api

ERPNext API Error Handling

Patterns for handling errors in API development. For syntax details, see erpnext-api-patterns .

Version: v14/v15/v16 compatible

API Error Handling Overview

┌─────────────────────────────────────────────────────────────────────┐ │ API ERROR HANDLING DECISION │ ├─────────────────────────────────────────────────────────────────────┤ │ │ │ Where is the error occurring? │ │ │ │ Server-side (Python)? │ │ ├── Validation error → frappe.throw() with clear message │ │ ├── Permission error → frappe.throw() + PermissionError │ │ ├── Not found → frappe.throw() + DoesNotExistError │ │ └── Unexpected → Log + generic error to client │ │ │ │ Client-side (JavaScript)? │ │ ├── frappe.call → Use error callback or .catch() │ │ └── frappe.xcall → Use try/catch with async/await │ │ │ │ External integration? │ │ └── requests library → try/except with specific exceptions │ │ │ └─────────────────────────────────────────────────────────────────────┘

HTTP Status Codes Reference

Code Meaning When Frappe Uses

200 Success Normal response

400 Bad Request Validation error

403 Forbidden Permission denied

404 Not Found Document doesn't exist

417 Expectation Failed frappe.throw() called

500 Server Error Unhandled exception

Server-Side Patterns

Basic Whitelisted Method

@frappe.whitelist() def update_status(docname, status): # Validate input if not docname: frappe.throw(_("Document name is required"), frappe.ValidationError)

if status not in ["Draft", "Submitted", "Cancelled"]:
    frappe.throw(_("Invalid status: {0}").format(status))

try:
    doc = frappe.get_doc("My DocType", docname)
    doc.status = status
    doc.save()
    return {"success": True, "name": doc.name}
except frappe.DoesNotExistError:
    frappe.throw(_("Document {0} not found").format(docname))
except frappe.PermissionError:
    frappe.throw(_("Permission denied"), frappe.PermissionError)

Bulk Operation with Partial Failure

@frappe.whitelist() def bulk_update(items): items = frappe.parse_json(items) results = {"success": [], "failed": []}

for item in items:
    try:
        doc = frappe.get_doc("Item", item["name"])
        doc.update(item)
        doc.save()
        results["success"].append(item["name"])
    except Exception as e:
        results["failed"].append({
            "name": item["name"],
            "error": str(e)
        })

frappe.db.commit()
return results

Client-Side Patterns

frappe.call Error Handling

frappe.call({ method: "myapp.api.update_status", args: { docname: "DOC-001", status: "Submitted" }, callback: function(r) { if (r.message && r.message.success) { frappe.show_alert({message: __("Updated"), indicator: "green"}); } }, error: function(r) { // Called on HTTP error or frappe.throw frappe.msgprint({ title: __("Error"), message: r.message || __("Operation failed"), indicator: "red" }); } });

async/await Pattern

async function updateDocument(docname, status) { try { const result = await frappe.xcall("myapp.api.update_status", { docname: docname, status: status }); return result; } catch (error) { console.error("API Error:", error); frappe.throw(__("Failed to update document")); } }

External API Pattern

import requests

def call_external_api(endpoint, data): try: response = requests.post( endpoint, json=data, timeout=30, headers={"Authorization": f"Bearer {get_api_key()}"} ) response.raise_for_status() return response.json() except requests.Timeout: frappe.log_error("External API timeout", "API Integration") frappe.throw(("External service timeout. Please try again.")) except requests.HTTPError as e: frappe.log_error(f"HTTP {e.response.status_code}", "API Integration") frappe.throw(("External service error")) except requests.RequestException as e: frappe.log_error(str(e), "API Integration") frappe.throw(_("Connection failed"))

Critical Rules

✅ ALWAYS

  • Validate input before processing

  • Use frappe.throw() for user-facing errors

  • Log unexpected errors with frappe.log_error()

  • Return structured responses from APIs

  • Handle both success and error in callbacks

❌ NEVER

  • Expose internal error details to users

  • Catch exceptions without logging

  • Return raw exception messages

  • Assume API calls will succeed

  • Skip input validation

Quick Reference: Error Responses

User-facing error (shows alert)

frappe.throw(_("Clear error message"))

Permission error (403)

frappe.throw(_("Not allowed"), frappe.PermissionError)

Validation error (400)

frappe.throw(_("Invalid input"), frappe.ValidationError)

Log error (no user message)

frappe.log_error(frappe.get_traceback(), "Error Title")

Reference Files

File Contents

patterns.md Detailed error handling patterns

examples.md Complete working examples

anti-patterns.md Common mistakes to avoid

See Also

  • erpnext-api-patterns

  • API implementation patterns

  • erpnext-syntax-whitelisted

  • Whitelisted method syntax

  • erpnext-errors-serverscripts

  • Server Script error handling

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

Coding

erpnext-code-interpreter

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

erpnext-syntax-jinja

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

erpnext-impl-controllers

No summary provided by upstream source.

Repository SourceNeeds Review