erpnext-errors-clientscripts

安装

npx skills add https://github.com/openaec-foundation/erpnext_anthropic_claude_development_skill_package --skill erpnext-errors-clientscripts
ERPNext Client Scripts - Error Handling
This skill covers error handling patterns for Client Scripts. For syntax, see
erpnext-syntax-clientscripts
. For implementation workflows, see
erpnext-impl-clientscripts
.
Version
v14/v15/v16 compatible
Main Decision: How to Handle the Error?
┌─────────────────────────────────────────────────────────────────────────┐
│ WHAT TYPE OF ERROR ARE YOU HANDLING? │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ ► Validation error (must prevent save)? │
│ └─► frappe.throw() in validate event │
│ │
│ ► Warning (inform user, allow continue)? │
│ └─► frappe.msgprint() with indicator │
│ │
│ ► Server call might fail? │
│ └─► try/catch with async/await OR callback error handling │
│ │
│ ► Field value invalid (not blocking)? │
│ └─► frm.set_intro() or field description │
│ │
│ ► Need to debug/trace? │
│ └─► console.log/warn/error + frappe.show_alert for dev │
│ │
│ ► Unexpected error in any code? │
│ └─► Wrap in try/catch, log error, show user-friendly message │
│ │
└─────────────────────────────────────────────────────────────────────────┘
Error Feedback Methods
Quick Reference
Method
Blocks Save?
User Action
Use For
frappe.throw()
✅ YES
Must dismiss
Validation errors
frappe.msgprint()
❌ NO
Must dismiss
Important info/warnings
frappe.show_alert()
❌ NO
Auto-dismiss
Success/info feedback
frm.set_intro()
❌ NO
None
Form-level warnings
frm.dashboard.set_headline()
❌ NO
None
Status indicators
console.error()
❌ NO
None
Debugging only
frappe.throw() - Blocking Error
// Basic throw - stops execution, prevents save
frappe
.
throw
(
__
(
'Customer is required'
)
)
;
// With title
frappe
.
throw
(
{
title
:
__
(
'Validation Error'
)
,
message
:
__
(
'Amount cannot be negative'
)
}
)
;
// With indicator color
frappe
.
throw
(
{
message
:
__
(
'Credit limit exceeded'
)
,
indicator
:
'red'
}
)
;
CRITICAL
Only use frappe.throw() in validate event to prevent save. Using it elsewhere stops script execution but doesn't prevent form actions. frappe.msgprint() - Non-Blocking Alert // Simple message frappe . msgprint ( __ ( 'Document will be processed' ) ) ; // With title and indicator frappe . msgprint ( { title : __ ( 'Warning' ) , message : __ ( 'Stock is running low' ) , indicator : 'orange' } ) ; // With primary action button frappe . msgprint ( { title : __ ( 'Confirm Action' ) , message : __ ( 'This will archive 50 records. Continue?' ) , primary_action : { label : __ ( 'Yes, Archive' ) , action : ( ) => { // perform action frappe . hide_msgprint ( ) ; } } } ) ; frappe.show_alert() - Toast Notification // Success (green, auto-dismiss) frappe . show_alert ( { message : __ ( 'Saved successfully' ) , indicator : 'green' } , 3 ) ; // 3 seconds // Warning (orange) frappe . show_alert ( { message : __ ( 'Some items are out of stock' ) , indicator : 'orange' } , 5 ) ; // Error (red) frappe . show_alert ( { message : __ ( 'Failed to fetch data' ) , indicator : 'red' } , 5 ) ; Error Handling Patterns Pattern 1: Synchronous Validation frappe . ui . form . on ( 'Sales Order' , { validate ( frm ) { // Multiple validations - collect errors let errors = [ ] ; if ( ! frm . doc . customer ) { errors . push ( __ ( 'Customer is required' ) ) ; } if ( frm . doc . grand_total <= 0 ) { errors . push ( __ ( 'Total must be greater than zero' ) ) ; } if ( ! frm . doc . items || frm . doc . items . length === 0 ) { errors . push ( __ ( 'At least one item is required' ) ) ; } // Throw all errors at once if ( errors . length

0 ) { frappe . throw ( { title : __ ( 'Validation Errors' ) , message : errors . join ( '
' ) } ) ; } } } ) ; Pattern 2: Async Server Call with Error Handling frappe . ui . form . on ( 'Sales Order' , { async customer ( frm ) { if ( ! frm . doc . customer ) return ; try { let r = await frappe . call ( { method : 'myapp.api.get_customer_details' , args : { customer : frm . doc . customer } } ) ; if ( r . message ) { frm . set_value ( 'credit_limit' , r . message . credit_limit ) ; } } catch ( error ) { console . error ( 'Failed to fetch customer:' , error ) ; frappe . show_alert ( { message : __ ( 'Could not load customer details' ) , indicator : 'red' } , 5 ) ; // Don't throw - allow user to continue } } } ) ; Pattern 3: Async Validation with Server Check frappe . ui . form . on ( 'Sales Order' , { async validate ( frm ) { // Server-side validation try { let r = await frappe . call ( { method : 'myapp.api.validate_order' , args : { customer : frm . doc . customer , total : frm . doc . grand_total } } ) ; if ( r . message && ! r . message . valid ) { frappe . throw ( { title : __ ( 'Validation Failed' ) , message : r . message . error } ) ; } } catch ( error ) { // Server error - decide: block or allow? console . error ( 'Validation call failed:' , error ) ; // Option 1: Block save on server error (safer) frappe . throw ( __ ( 'Could not validate. Please try again.' ) ) ; // Option 2: Allow save with warning (use with caution) // frappe.show_alert({ // message: __('Validation skipped due to server error'), // indicator: 'orange' // }, 5); } } } ) ; Pattern 4: frappe.call Callback Error Handling // When not using async/await frappe . call ( { method : 'myapp.api.process_data' , args : { doc_name : frm . doc . name } , freeze : true , freeze_message : __ ( 'Processing...' ) , callback : ( r ) => { if ( r . message ) { frappe . show_alert ( { message : __ ( 'Processing complete' ) , indicator : 'green' } ) ; frm . reload_doc ( ) ; } } , error : ( r ) => { // Server returned error (4xx, 5xx) console . error ( 'API Error:' , r ) ; frappe . msgprint ( { title : __ ( 'Error' ) , message : __ ( 'Processing failed. Please try again.' ) , indicator : 'red' } ) ; } } ) ; Pattern 5: Child Table Validation frappe . ui . form . on ( 'Sales Invoice' , { validate ( frm ) { let errors = [ ] ; ( frm . doc . items || [ ] ) . forEach ( ( row , idx ) => { if ( ! row . item_code ) { errors . push ( __ ( 'Row {0}: Item is required' , [ idx + 1 ] ) ) ; } if ( row . qty <= 0 ) { errors . push ( __ ( 'Row {0}: Quantity must be positive' , [ idx + 1 ] ) ) ; } if ( row . rate < 0 ) { errors . push ( __ ( 'Row {0}: Rate cannot be negative' , [ idx + 1 ] ) ) ; } } ) ; if ( errors . length

0 ) { frappe . throw ( { title : __ ( 'Item Errors' ) , message : errors . join ( '
' ) } ) ; } } } ) ; Pattern 6: Graceful Degradation frappe . ui . form . on ( 'Sales Order' , { async refresh ( frm ) { // Try to load extra data, but don't fail if unavailable try { let stock = await frappe . call ( { method : 'myapp.api.get_stock_summary' , args : { items : frm . doc . items . map ( r => r . item_code ) } } ) ; if ( stock . message ) { render_stock_dashboard ( frm , stock . message ) ; } } catch ( error ) { // Log but don't disturb user console . warn ( 'Stock dashboard unavailable:' , error ) ; // Optionally show subtle indicator frm . dashboard . set_headline ( __ ( 'Stock info unavailable' ) , 'orange' ) ; } } } ) ; See : references/patterns.md for more error handling patterns. Debugging Techniques Console Logging // Development debugging frappe . ui . form . on ( 'Sales Order' , { customer ( frm ) { console . log ( 'Customer changed:' , frm . doc . customer ) ; console . log ( 'Full doc:' , JSON . parse ( JSON . stringify ( frm . doc ) ) ) ; // Trace child table console . table ( frm . doc . items ) ; } } ) ; Conditional Debugging // Only log in development const DEBUG = frappe . boot . developer_mode ; function debugLog ( ... args ) { if ( DEBUG ) { console . log ( '[MyApp]' , ... args ) ; } } frappe . ui . form . on ( 'Sales Order' , { validate ( frm ) { debugLog ( 'Validating:' , frm . doc . name ) ; // validation logic } } ) ; Error Stack Traces try { riskyOperation ( ) ; } catch ( error ) { console . error ( 'Error details:' , { message : error . message , stack : error . stack , doc : frm . doc . name } ) ; // User-friendly message (no technical details) frappe . msgprint ( { title : __ ( 'Error' ) , message : __ ( 'An unexpected error occurred. Please contact support.' ) , indicator : 'red' } ) ; } Critical Rules ✅ ALWAYS Wrap async calls in try/catch - Uncaught Promise rejections crash silently Use __() for error messages - All user-facing text must be translatable Log errors to console - Helps debugging without exposing to users Collect multiple validation errors - Don't throw on first error Provide actionable error messages - Tell user how to fix it ❌ NEVER Don't expose technical errors to users - Catch and translate Don't use frappe.throw() outside validate - It stops execution but doesn't prevent save Don't ignore server call failures - Always handle error callback Don't use alert() or confirm() - Use frappe methods instead Don't leave console.log in production - Use conditional debugging Quick Reference: Error Message Quality // ❌ BAD - Technical, not actionable frappe . throw ( 'NullPointerException in line 42' ) ; frappe . throw ( 'Query failed' ) ; frappe . throw ( 'Error' ) ; // ✅ GOOD - Clear, actionable frappe . throw ( __ ( 'Please select a customer before adding items' ) ) ; frappe . throw ( __ ( 'Amount {0} exceeds credit limit of {1}' , [ amount , limit ] ) ) ; frappe . throw ( __ ( 'Could not save. Please check your internet connection and try again.' ) ) ; Reference Files File Contents references/patterns.md Complete error handling patterns references/examples.md Full working examples references/anti-patterns.md Common mistakes to avoid See Also erpnext-syntax-clientscripts - Client Script syntax erpnext-impl-clientscripts - Implementation workflows erpnext-errors-serverscripts - Server-side error handling

返回排行榜