Skip to content

Conditions: when, preconditions, abort_unless

Three step fields take conditions: when, preconditions and abort_unless. This page gives the one way conditions are written, the operators, what each comparison does with values of different kinds, and where the three fields differ. Every result in the tables comes from a run.

Field Allowed on Checked when If a condition is false
when auto, agent, guard The steps it depends on are settled. The step is skipped.
preconditions auto, agent Someone tries to run the step. The step is blocked. It can be tried again.
abort_unless guard The guard runs. The run is aborted.

Each field takes one condition, or a list. In a list, every condition must be true. preconditions must be a list, even for one condition.

<path> <operator> <value>
<path>
  • Path. A step’s name, a dot, and a field of that step’s output: classify.category. Deeper fields and list items follow with more dots: order.customer.name, order.items.0.sku. Two other roots exist: run.params.<name> for the run’s parameters, and $settlement.<step>.<field>, described below.
  • Operator. One of ==, !=, >, >=, <, <=.
  • Value. Text in single or double quotes, a number, true, false or null.
  • A path alone is true when its value counts as true. preconditions does not accept this form.

Paths here are shorter than in input_map and in template expressions. There, a step’s output is context.resources.classify.category. In a condition that form is refused:

Invalid workflow: Step 'c': 'when' references step 'context' which is not in its depends_on [a]. Add it to depends_on or use 'run.params.*'. (step at line 11)

There is no and, or or not. For “and”, use a list:

when:
- 'extract.found == true'
- 'resolve.store != null'

A condition that contains and or or is refused, and the message shows the list to write in its place:

Invalid workflow: Step 'c': 'when' uses unsupported 'and' — write it as a list:
when:
- "a.x == 1"
- "a.y == 2" (step at line 11)

For “or”, use two steps, or a step with trigger_rule: one_success. See Order and routing.

These results are from one run. A step named data produced this output:

{
"n": 5,
"numstr": "5",
"price": 9.99,
"neg": -4.2,
"s": "billing",
"t": true,
"f": false,
"z": 0,
"e": "",
"nul": null,
"list": ["a"],
"empty_list": [],
"obj": { "k": 1 }
}

Each condition below was the when of its own step. true means that step ran; false means it was skipped.

Comparing numbers

Condition Result Note
data.n == 5 true n is the number 5.
data.n == '5' false A number is never equal to text.
data.numstr == '5' true numstr is the text 5.
data.numstr == 5 false Text is never equal to a number.
data.n != 5 false
data.n > 3 true
data.n >= 5 true
data.n < 5 false
data.n <= 5 true
data.price >= 9.99 true
data.neg < -1 true neg is −4.2.
data.s > 3 false >, >=, < and <= are false unless both sides are numbers.

Comparing text and true or false

Condition Result Note
data.s == 'billing' true
data.s == "billing" true Double quotes work too.
data.s == billing true A word without quotes is read as text.
data.s == 'Billing' false Upper and lower case differ.
data.s != 'billing' false
data.s == 'a >= b' false An operator inside quotes is part of the text.
data.t == true true
data.f == false true
data.t == 'true' false true is not the text true.

A path alone

Condition Result Note
data.t true true
data.f false false
data.z false The number 0.
data.e false Empty text.
data.nul false null
data.missing false There is no such field.
data.list true A list, even an empty one, counts as true.
data.empty_list true
data.obj true A map counts as true.

Missing values and null

Condition Result Note
data.missing == null true True when the value is missing or null.
data.nul == null true
data.missing != null false
data.n != null true True when the value is present and not null.
data.missing == 'x' false Any other comparison with a missing value is false.
data.missing != 'x' false This one too: != does not become true because the value is missing.
data.missing > 1 false

Deeper paths, run parameters and $settlement

Condition Result Note
data.obj.k == 1 true
data.list.0 == 'a' true A number selects an item of a list.
run.params.mode == 'live' true The run was started with mode: live.
run.params.absent == null true
$settlement.data.failed == false true
$settlement.data.settled_by_default == false true
$settlement.data.validation_rejections == 0 true

The table above is for when. The same conditions were run as preconditions and as abort_unless. They agree except here:

Condition form when preconditions abort_unless
A path alone Accepted Refused when the file is loaded Accepted
<path> == null True for a missing or null value False False
run.params.<name> Read Not read: the condition is false Not read: the run fails
A path that finds nothing The condition is false The condition is false The run fails. It is not recorded as aborted.
A path to a step not in depends_on Refused when the file is loaded Accepted Accepted

So a test for a missing value belongs in when. In preconditions and abort_unless, null is not understood: == null is false even when the value is null.

A path alone in preconditions is refused:

Step 'c17': precondition 'data.t' must be a comparison (e.g. "step.field >= 1"). (step at line 95)

when. The step is listed as skipped, and the record keeps each condition with the value it found:

{
"kind": "when_false",
"expression": "data.n == '5'",
"leaves": [{ "leaf": "data.n == '5'", "lhs_present": true, "resolved_value": 5, "passed": false }]
}

preconditions. Running the step returns status: blocked, and says which condition failed and what it found:

status: blocked
Precondition failed for step 'c04'.
Precondition failed: 'data.n != 5'. Resolved value: 5.

The run stays open. When the step does run, its record shows each precondition and the value it found: preconditions: data.n == 5 → true (5).

abort_unless. The run ends as aborted. The guard’s record lists every condition, and the steps after it are skipped:

Phase: aborted
Sealed by: guard_abort (check)
Skipped: check, after
check: guard_abort

When a guard’s path finds nothing, the run ends as failed:

Guard step 'check' failed with a resolution error. Run is terminated.

$settlement.<step> says how a step was settled. It has an entry for every step that completed or failed, and none for a step that was skipped or has not finished.

Field Type Meaning
failed true or false The step failed.
settled_by_default true or false The step was completed with its declared default answer, after too many refused answers.
validation_rejections whole number How many of the step’s answers were refused before it settled.
cleanup:
description: Undo the extract if it failed.
execution: auto
depends_on: [extract, transform]
trigger_rule: all_done
when: '$settlement.extract.failed == true'
handler: compensate_extract

trigger_rule: all_done is needed here. Under the default rule the step is skipped as soon as a step it depends on fails, so the condition could never be true. The loader refuses that combination:

Invalid workflow: Step 'c': 'when' condition "$settlement.a.failed == true" can never be true — under the default 'all_success' trigger rule, 'a' can never be in failed_steps when this step is evaluated (…). To run this step when 'a' fails, set trigger_rule to one of: all_done, one_failed, all_failed. (step at line 11)

In input_map and template expressions, the same values are at context.resources.$settlement.<step>.<field>.

Because a skipped step has no entry, $settlement.<step>.failed == null in a when is true exactly when that step was skipped or has not settled.

  • A when path must start with run.params, or with a step listed in the step’s own depends_on. A $settlement path must name such a step too: '$settlement' paths must reference a direct dependency (one-hop rule).
  • preconditions must be a list: 'preconditions' must be an array of strings.
  • A when list must not be empty: 'when' array must not be empty.
  • A step cannot be named run, context or $settlement: Step name 'run' is reserved and cannot be used as a step identifier.
  • Field names are not checked. A misspelt field is a path that finds nothing.