Skip to main content

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​

  1. The activity's token does not advance. The activity stays in its current state (e.g., claimed for a userTask, executing for a serviceTask).
  2. A ValidationError event is dispatched.
  3. If a boundaryEvent with an errorEventDefinition is attached to the activity, it fires and the token follows that path.
  4. 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" />
tip

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:

VariableDescription
_validation.pathJSON path to the invalid field, e.g., "decision"
_validation.valueThe value that failed validation
_validation.expectedHuman-readable description of what was expected
_validation.errorsFull 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​

ScenarioResult
POST /instances with invalid variable400 response, instance not created
POST /tasks/:id/complete with invalid output400 response, task stays claimed
Invalid output from a service taskValidationError event fired
Boundary event presentToken follows the boundary path
No boundary eventInstance moves to error state
POST /instances/:id/resumeToken restarts from frozen activity