Error Handling
When validation fails during process execution, Stateway fires a BPMN error event with errorCode = "ValidationError". You can catch it with a boundary event on the failing activity, or let it propagate to put the instance in error state.
What Happens on Failure
- The activity's token does not advance. The activity stays in its current state (e.g.,
claimedfor a userTask,executingfor a serviceTask). - A
ValidationErrorevent is dispatched. - If a
boundaryEventwith anerrorEventDefinitionis attached to the activity, it fires and the token follows that path. - If no boundary event catches it, the instance moves to
status: 'error'and the token is frozen.
Boundary Event Pattern
Attach a boundary error event to the failing activity:
<!-- At <bpmn:definitions> level — declare the error -->
<bpmn:error id="ValidationError" name="Validation Error" errorCode="ValidationError" />
<!-- The failing task -->
<bpmn:userTask id="approve" name="Approve Loan">
<!-- ... ioSpecification ... -->
</bpmn:userTask>
<!-- Boundary event catches ValidationError from "approve" -->
<bpmn:boundaryEvent id="approveValidationError" attachedToRef="approve" cancelActivity="true">
<bpmn:errorEventDefinition errorRef="ValidationError" />
<bpmn:outgoing>flow_to_correction</bpmn:outgoing>
</bpmn:boundaryEvent>
<!-- Route to a correction task -->
<bpmn:userTask id="correctApproval" name="Correct and Resubmit">
<bpmn:incoming>flow_to_correction</bpmn:incoming>
<bpmn:outgoing>flow_retry</bpmn:outgoing>
</bpmn:userTask>
<bpmn:sequenceFlow id="flow_to_correction" sourceRef="approveValidationError" targetRef="correctApproval" />
<bpmn:sequenceFlow id="flow_retry" sourceRef="correctApproval" targetRef="approve" />
cancelActivity="true" (the default) cancels the original task when the boundary event fires. The token moves to the correction task; the original task is no longer active.
Error Context Variables
When validation fails (boundary event or not), Stateway injects error context into instance.variables:
| Variable | Description |
|---|---|
_validation.path | JSON path to the invalid field, e.g., "decision" |
_validation.value | The value that failed validation |
_validation.expected | Human-readable description of what was expected |
_validation.errors | Full array of { path, value, message } error objects |
You can use these in correction forms or gateway conditions:
<bpmn:exclusiveGateway id="checkError" name="What failed?" />
<bpmn:sequenceFlow sourceRef="checkError" targetRef="fixEnum"
condition="{{ _validation.path == 'decision' }}" />
<bpmn:sequenceFlow sourceRef="checkError" targetRef="fixObject"
condition="{{ _validation.path.startsWith('applicant') }}" />
Uncaught Errors — Instance Recovery
If no boundary event is present, the instance reaches status: 'error':
curl https://api.stateway.io/v1/instances/inst_01... \
-H "X-API-Key: sw_live_your_key"
{
"data": {
"id": "inst_01...",
"status": "error",
"variables": {
"_validation": {
"path": "decision",
"value": "APPROVED",
"expected": "one of: draft, approved, rejected"
}
}
}
}
After investigating and correcting the underlying issue, resume the instance:
curl -X POST https://api.stateway.io/v1/instances/inst_01.../resume \
-H "X-API-Key: sw_live_your_key"
The token restarts from the frozen activity. The user can then resubmit the corrected payload.
DataStore Not Found
When a DataStore lookup finds no matching instance and onNotFound="error" is set, the same ValidationError flow applies. The boundary event pattern works identically:
<bpmn:serviceTask id="lookup" name="Load Original Loan">
<bpmn:dataInputAssociation>
<bpmn:sourceRef>LoanStoreRef</bpmn:sourceRef>
<bpmn:targetRef>OriginalLoanRef</bpmn:targetRef>
</bpmn:dataInputAssociation>
</bpmn:serviceTask>
<bpmn:boundaryEvent id="loanNotFound" attachedToRef="lookup" cancelActivity="true">
<bpmn:errorEventDefinition errorRef="ValidationError" />
<bpmn:outgoing>flow_to_rejection</bpmn:outgoing>
</bpmn:boundaryEvent>
Use onNotFound="null" if you want execution to continue with the target variable set to null instead of raising an error.
Error Reference Declaration
Declare the bpmn:error element at the definitions level:
<bpmn:error id="ValidationError" name="Validation Error" errorCode="ValidationError" />
This is optional — Stateway matches on errorCode regardless — but recommended for BPMN modeler compatibility (Camunda Modeler, bpmn.io will render the error name in the diagram).
Summary
| Scenario | Result |
|---|---|
POST /instances with invalid variable | 400 response, instance not created |
POST /tasks/:id/complete with invalid output | 400 response, task stays claimed |
| Invalid output from a service task | ValidationError event fired |
| Boundary event present | Token follows the boundary path |
| No boundary event | Instance moves to error state |
POST /instances/:id/resume | Token restarts from frozen activity |