@sfdxy/mule-lint 1.21.0 → 1.22.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/package.json +1 -1
- package/dist/src/mcp/prompts/index.d.ts +1 -1
- package/dist/src/mcp/prompts/index.d.ts.map +1 -1
- package/dist/src/mcp/prompts/index.js +62 -1
- package/dist/src/mcp/prompts/index.js.map +1 -1
- package/dist/src/mcp/resources/index.js +114 -16
- package/dist/src/mcp/resources/index.js.map +1 -1
- package/dist/src/mcp/tools/getRuleDetails.d.ts.map +1 -1
- package/dist/src/mcp/tools/getRuleDetails.js +30 -1
- package/dist/src/mcp/tools/getRuleDetails.js.map +1 -1
- package/docs/README.md +87 -27
- package/docs/best-practices/ci-cd.md +135 -0
- package/docs/best-practices/connector-patterns.md +253 -0
- package/docs/best-practices/dataweave-patterns.md +370 -0
- package/docs/best-practices/deployment-2026.md +171 -0
- package/docs/best-practices/error-handling.md +277 -0
- package/docs/best-practices/event-driven-patterns.md +424 -0
- package/docs/best-practices/logging.md +163 -0
- package/docs/best-practices/mulesoft-best-practices.md +72 -865
- package/docs/best-practices/performance.md +273 -0
- package/docs/best-practices/security.md +181 -0
- package/docs/best-practices/testing.md +190 -0
- package/docs/best-practices/variable-contracts.md +191 -0
- package/package.json +1 -1
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
# Error Handling Best Practices
|
|
2
|
+
|
|
3
|
+
> **Applies to:** HTTP APIs · Event-Driven · Batch · All
|
|
4
|
+
> **Related Rules:** `MULE-001` · `MULE-003` · `MULE-005` · `MULE-007` · `MULE-009` · `ERR-001` · `ERR-002` · `ERR-003` · `ERR-004`
|
|
5
|
+
> **Last Updated:** April 2026
|
|
6
|
+
|
|
7
|
+
## When to Read This
|
|
8
|
+
|
|
9
|
+
Read this when designing, implementing, or reviewing error handling for any Mule 4 application — whether it exposes an HTTP API, listens to platform events, or processes batch data.
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## Decision Matrix
|
|
14
|
+
|
|
15
|
+
| Project Type | Error Handler Location | Sets `httpStatus`? | Error Response Format | Correlation ID Source |
|
|
16
|
+
| ------------------------------ | ------------------------------------------------------------ | ------------------------------------------ | ---------------------------------------- | ------------------------------------------------------------- |
|
|
17
|
+
| **HTTP API (SAPI/Experience)** | Dedicated `error-handling.xml` or `global-error-handler.xml` | ✅ Yes — sets `httpStatus` in every branch | JSON error envelope → HTTP response | `attributes.headers.'x-correlation-id' default correlationId` |
|
|
18
|
+
| **Event-Driven (PAPI)** | Part of `error-handling.xml` | ❌ No — no HTTP response layer | Error payload → writeback + notification | Platform Event `EventUuid` |
|
|
19
|
+
| **Batch / Scheduler** | Inline or referenced `error-handler` | Depends on trigger | Logged error + retry/dead-letter | Generated UUID or scheduler ID |
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## Key Principles
|
|
24
|
+
|
|
25
|
+
1. **Every flow needs error handling** — either an explicit `<error-handler>` or a `ref` to a global one
|
|
26
|
+
2. **Set HTTP status codes** — always set `httpStatus` variable for API responses (HTTP APIs only)
|
|
27
|
+
3. **Include correlation ID** — enable distributed tracing across all API layers
|
|
28
|
+
4. **Be specific about error types** — avoid catching `type="ANY"` except as the **last** handler in the chain
|
|
29
|
+
5. **Consistent error envelope** — use the same response shape for all error types
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
## Patterns
|
|
34
|
+
|
|
35
|
+
### Pattern 1: Global Error Handler (HTTP APIs)
|
|
36
|
+
|
|
37
|
+
**Use when:** building any HTTP-facing API (System API, Experience API, or APIKit-based Process API).
|
|
38
|
+
|
|
39
|
+
Every error branch should:
|
|
40
|
+
|
|
41
|
+
- Set `httpStatus` via `ee:set-variable`
|
|
42
|
+
- Build a JSON error response with `environment`, `correlationId`, `error`, and `message`
|
|
43
|
+
- Place `type="ANY"` as the **last** branch (catch-all)
|
|
44
|
+
|
|
45
|
+
```xml
|
|
46
|
+
<!-- src/main/mule/common/error-handling.xml -->
|
|
47
|
+
<error-handler name="global-error-handler">
|
|
48
|
+
|
|
49
|
+
<!-- 400 Bad Request -->
|
|
50
|
+
<on-error-propagate type="APIKIT:BAD_REQUEST">
|
|
51
|
+
<ee:transform>
|
|
52
|
+
<ee:message>
|
|
53
|
+
<ee:set-payload><![CDATA[%dw 2.0
|
|
54
|
+
output application/json
|
|
55
|
+
---
|
|
56
|
+
{
|
|
57
|
+
environment: p('mule.env'),
|
|
58
|
+
correlationId: (vars.correlationId default "") as String,
|
|
59
|
+
error: "InvalidInput",
|
|
60
|
+
message: error.detailedDescription default "Bad request"
|
|
61
|
+
}]]></ee:set-payload>
|
|
62
|
+
</ee:message>
|
|
63
|
+
<ee:variables>
|
|
64
|
+
<ee:set-variable variableName="httpStatus">400</ee:set-variable>
|
|
65
|
+
</ee:variables>
|
|
66
|
+
</ee:transform>
|
|
67
|
+
</on-error-propagate>
|
|
68
|
+
|
|
69
|
+
<!-- 404 Not Found -->
|
|
70
|
+
<on-error-propagate type="APIKIT:NOT_FOUND">
|
|
71
|
+
<!-- ... set httpStatus to 404, build response ... -->
|
|
72
|
+
</on-error-propagate>
|
|
73
|
+
|
|
74
|
+
<!-- 500 Internal Server Error (catch-all — MUST be last) -->
|
|
75
|
+
<on-error-propagate type="ANY" enableNotifications="true" logException="true">
|
|
76
|
+
<ee:transform>
|
|
77
|
+
<ee:message>
|
|
78
|
+
<ee:set-payload><![CDATA[%dw 2.0
|
|
79
|
+
output application/json
|
|
80
|
+
---
|
|
81
|
+
{
|
|
82
|
+
environment: p('mule.env'),
|
|
83
|
+
correlationId: (vars.correlationId default "") as String,
|
|
84
|
+
error: "InternalError",
|
|
85
|
+
message: error.detailedDescription default "An unexpected error occurred."
|
|
86
|
+
}]]></ee:set-payload>
|
|
87
|
+
</ee:message>
|
|
88
|
+
<ee:variables>
|
|
89
|
+
<ee:set-variable variableName="httpStatus">500</ee:set-variable>
|
|
90
|
+
</ee:variables>
|
|
91
|
+
</ee:transform>
|
|
92
|
+
</on-error-propagate>
|
|
93
|
+
|
|
94
|
+
</error-handler>
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
Reference it from the main API flow:
|
|
98
|
+
|
|
99
|
+
```xml
|
|
100
|
+
<flow name="api-main">
|
|
101
|
+
<http:listener config-ref="httpListenerConfig" path="/api/*">
|
|
102
|
+
<http:response statusCode="#[vars.httpStatus default 200]"/>
|
|
103
|
+
<http:error-response statusCode="#[vars.httpStatus default 500]">
|
|
104
|
+
<http:body><![CDATA[#[payload]]]></http:body>
|
|
105
|
+
</http:error-response>
|
|
106
|
+
</http:listener>
|
|
107
|
+
<apikit:router config-ref="api-config"/>
|
|
108
|
+
<error-handler ref="global-error-handler"/>
|
|
109
|
+
</flow>
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
### Pattern 2: Error Handler (Event-Driven)
|
|
113
|
+
|
|
114
|
+
**Use when:** building a process API driven by Salesforce Platform Events, Anypoint MQ, or Kafka.
|
|
115
|
+
|
|
116
|
+
No `httpStatus`. Instead, errors trigger:
|
|
117
|
+
|
|
118
|
+
1. Structured error payload build
|
|
119
|
+
2. Writeback to source system (e.g., SF error field update)
|
|
120
|
+
3. Notification dispatch (email, Slack, NetSuite error log)
|
|
121
|
+
|
|
122
|
+
```xml
|
|
123
|
+
<error-handler name="global-error-handler">
|
|
124
|
+
<on-error-propagate enableNotifications="true" logException="true">
|
|
125
|
+
<!-- Build structured error payload -->
|
|
126
|
+
<ee:transform>
|
|
127
|
+
<ee:variables>
|
|
128
|
+
<ee:set-variable variableName="errorPayload"
|
|
129
|
+
resource="dwl/transforms/error-payload.dwl"/>
|
|
130
|
+
</ee:variables>
|
|
131
|
+
</ee:transform>
|
|
132
|
+
<!-- Log the error -->
|
|
133
|
+
<logger level="ERROR" message="#[vars.errorPayload]"
|
|
134
|
+
category="#[vars.logCategory default 'com.myorg.papi']"/>
|
|
135
|
+
<!-- Writeback error status to source system -->
|
|
136
|
+
<flow-ref name="sf-writeback-subflow"/>
|
|
137
|
+
<!-- Dispatch notifications -->
|
|
138
|
+
<flow-ref name="notification-router-subflow"/>
|
|
139
|
+
</on-error-propagate>
|
|
140
|
+
</error-handler>
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
### Pattern 3: Try Scope for Risky Operations
|
|
144
|
+
|
|
145
|
+
**Use when:** a flow makes 2+ external calls (HTTP requests, DB operations, connector calls).
|
|
146
|
+
|
|
147
|
+
```xml
|
|
148
|
+
<!-- ❌ Bad — multiple calls without isolation -->
|
|
149
|
+
<flow name="process-order-flow">
|
|
150
|
+
<http:request config-ref="API"/>
|
|
151
|
+
<db:insert config-ref="Database"/>
|
|
152
|
+
</flow>
|
|
153
|
+
|
|
154
|
+
<!-- ✅ Good — each risky operation isolated -->
|
|
155
|
+
<flow name="process-order-flow">
|
|
156
|
+
<try>
|
|
157
|
+
<http:request config-ref="API"/>
|
|
158
|
+
<error-handler>
|
|
159
|
+
<on-error-propagate type="HTTP:CONNECTIVITY">
|
|
160
|
+
<logger category="com.myorg" level="ERROR"
|
|
161
|
+
message="#['API call failed: ' ++ error.description]"/>
|
|
162
|
+
</on-error-propagate>
|
|
163
|
+
</error-handler>
|
|
164
|
+
</try>
|
|
165
|
+
<try>
|
|
166
|
+
<db:insert config-ref="Database"/>
|
|
167
|
+
<error-handler>...</error-handler>
|
|
168
|
+
</try>
|
|
169
|
+
</flow>
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
### Pattern 4: Centralized Error Log Object (CRM Writeback)
|
|
173
|
+
|
|
174
|
+
**Use when:** integration errors need visibility in the CRM for operations teams (multi-system integrations where business users manage error resolution).
|
|
175
|
+
|
|
176
|
+
**Do NOT use when:** it's an internal-only SAPI where standard logging dashboards suffice, or for transient errors that will auto-recover via retry.
|
|
177
|
+
|
|
178
|
+
| Scenario | CRM Error Log? | Why |
|
|
179
|
+
| ---------------------------------------- | -------------- | ----------------------------------------------- |
|
|
180
|
+
| Multi-system sync (CRM ↔ ERP ↔ Payments) | ✅ | Ops needs single-pane visibility |
|
|
181
|
+
| Internal SAPI with no business users | ❌ | Logging + dashboards sufficient |
|
|
182
|
+
| Transient error (retry will succeed) | ❌ | Handle with retry, not logging |
|
|
183
|
+
| Real-time latency-sensitive flow | ⚠️ | Extra HTTP call per error; make async if needed |
|
|
184
|
+
|
|
185
|
+
**Standardized error payload contract:**
|
|
186
|
+
|
|
187
|
+
```xml
|
|
188
|
+
<!-- Every caller builds this shape before calling errorLogFlow -->
|
|
189
|
+
<ee:transform doc:name="Error Payload">
|
|
190
|
+
<ee:set-payload><![CDATA[%dw 2.0
|
|
191
|
+
output application/json
|
|
192
|
+
---
|
|
193
|
+
{
|
|
194
|
+
"Subject": "Failed to sync Invoice to ERP",
|
|
195
|
+
"Error": error.detailedDescription default "",
|
|
196
|
+
"Link": p('links.crm.record') ++ vars.recordId ++ "/view",
|
|
197
|
+
"RecordId": vars.recordId default ""
|
|
198
|
+
}]]></ee:set-payload>
|
|
199
|
+
</ee:transform>
|
|
200
|
+
<flow-ref name="errorLogFlow"/>
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
**The error log flow — with self-healing handler:**
|
|
204
|
+
|
|
205
|
+
```xml
|
|
206
|
+
<flow name="errorLogFlow">
|
|
207
|
+
<ee:transform doc:name="Map to CRM Error Object">
|
|
208
|
+
<ee:set-payload><![CDATA[%dw 2.0
|
|
209
|
+
output application/json
|
|
210
|
+
---
|
|
211
|
+
{
|
|
212
|
+
"Application__c": "Mulesoft",
|
|
213
|
+
"Type__c": "Integration",
|
|
214
|
+
"Stacktrace__c": payload.Error default "",
|
|
215
|
+
"Record_Link__c": payload.Link default "",
|
|
216
|
+
"Record_Id__c": payload.RecordId default "",
|
|
217
|
+
"Subject__c": payload.Subject default ""
|
|
218
|
+
}]]></ee:set-payload>
|
|
219
|
+
</ee:transform>
|
|
220
|
+
|
|
221
|
+
<http:request method="POST" config-ref="SAPI_Config" path="/Error_Log__c"/>
|
|
222
|
+
|
|
223
|
+
<error-handler>
|
|
224
|
+
<!-- CRITICAL: Must NEVER throw — prevents infinite loop -->
|
|
225
|
+
<on-error-continue enableNotifications="true" logException="false">
|
|
226
|
+
<logger level="WARN"
|
|
227
|
+
message="Error logging failed: #[error.detailedDescription]"/>
|
|
228
|
+
</on-error-continue>
|
|
229
|
+
</error-handler>
|
|
230
|
+
</flow>
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
> ⚠️ **Self-healing rule:** The `errorLogFlow` **must never propagate an error**. If it does, the parent flow's error handler re-enters the error log → infinite loop. Always use `on-error-continue`.
|
|
234
|
+
|
|
235
|
+
---
|
|
236
|
+
|
|
237
|
+
## Connector-Specific Error Types
|
|
238
|
+
|
|
239
|
+
### Salesforce Connector (11.3.0+)
|
|
240
|
+
|
|
241
|
+
| Error Type | HTTP Status | Description |
|
|
242
|
+
| ------------------------------------- | ----------- | ---------------------------- |
|
|
243
|
+
| `SALESFORCE:CONNECTIVITY` | 502 | Connection failure to SF org |
|
|
244
|
+
| `SALESFORCE:INVALID_INPUT` | 400 | DML/SOQL validation error |
|
|
245
|
+
| `SALESFORCE:INVALID_RESPONSE` | 400 | Unparseable SF response |
|
|
246
|
+
| `SALESFORCE:FAULTY_RESPONSE` | 400 | SF returned error body |
|
|
247
|
+
| `SALESFORCE:NOT_FOUND` | 404 | Record not found |
|
|
248
|
+
| `SALESFORCE:TIMEOUT` | 504 | Connector timed out |
|
|
249
|
+
| `SALESFORCE:LIMIT_EXCEEDED` | 429 | API governor limit hit |
|
|
250
|
+
| `SALESFORCE:INSUFFICIENT_PERMISSIONS` | 403 | Permission denied |
|
|
251
|
+
|
|
252
|
+
### APIKit Error Types
|
|
253
|
+
|
|
254
|
+
| Error Type | HTTP Status | Description |
|
|
255
|
+
| ------------------------------- | ----------- | --------------------------- |
|
|
256
|
+
| `APIKIT:BAD_REQUEST` | 400 | Schema validation failure |
|
|
257
|
+
| `APIKIT:NOT_FOUND` | 404 | Unknown URI path |
|
|
258
|
+
| `APIKIT:METHOD_NOT_ALLOWED` | 405 | HTTP verb not in RAML |
|
|
259
|
+
| `APIKIT:NOT_ACCEPTABLE` | 406 | Content negotiation failure |
|
|
260
|
+
| `APIKIT:UNSUPPORTED_MEDIA_TYPE` | 415 | Wrong Content-Type |
|
|
261
|
+
|
|
262
|
+
---
|
|
263
|
+
|
|
264
|
+
## Checklist
|
|
265
|
+
|
|
266
|
+
- [ ] Global error handler defined in a dedicated XML file
|
|
267
|
+
- [ ] Every flow has `<error-handler ref="global-error-handler"/>` or explicit handler
|
|
268
|
+
- [ ] `httpStatus` set in every error branch (HTTP APIs only)
|
|
269
|
+
- [ ] `correlationId` included in every error response
|
|
270
|
+
- [ ] `type="ANY"` is the **last** handler in the chain
|
|
271
|
+
- [ ] Connector-specific error types handled before generic ones
|
|
272
|
+
- [ ] Error response shape is consistent across all branches
|
|
273
|
+
- [ ] Error log flow uses `on-error-continue` — never cascades (Pattern 4)
|
|
274
|
+
|
|
275
|
+
---
|
|
276
|
+
|
|
277
|
+
**See also:** [Variable Contracts](variable-contracts.md) · [Logging](logging.md) · [Rules Catalog](rules-catalog.md)
|