Appearance
Fallback records
When a definition's Context AND-match yields no row, the step is a silent no-op by default. Fallback Records let you substitute a chosen entity row instead, with multi-level granularity for progressively-failing matches.
This page covers what fallbacks do, when to configure them, and how the levels work.
Behavior described from UI labels
The end-to-end behavior of multi-level fallbacks is documented from the UI labels in the Manage Fallbacks modal. The exact runtime semantics at each level haven't been fully behaviorally probed. Treat this section as authoritative on configuration; some runtime nuances may differ slightly.
Without fallbacks: the silent no-op
By default, a Mapping step whose Context AND-match yields no row does nothing:
- No fields written.
- No errors logged.
- No indication to the reviewer that the step even ran.
The cascade continues; subsequent steps run with whatever state existed. The reviewer sees fields that didn't update and assumes the workflow didn't fire — when in fact the workflow ran, this step had nothing to match, and the next step ran with the unchanged state.
This is the single most common source of "the cascade didn't fire" confusion. The cascade did fire — this step just found no row.
With fallbacks: substitute a chosen row
A Fallback Record is a specific entity row picked in advance. When the lookup yields no match, the engine uses that row instead of the no-op.
Configured per definition, via the Fallbacks button on each definition's card. The fallback modal lets you pick a substitute row from the entity's existing records.
Multi-level fallbacks
For definitions with multiple Context columns, the fallback system has levels. Each level intercepts a different stage of the match failure.
For a definition with N Context columns, there are up to N+1 fallback levels:
- Level 0 — the first Context column fails to match anything. Available for any definition with at least one Context column.
- Level 1 — the first Context column matches some rows, but the second column doesn't narrow further.
- Level 2 — the first two columns match, but the third doesn't narrow further.
- … and so on.
Each level can be configured independently, with its own substitute row chosen.
Walking an example
Imagine a definition with three Context columns: customerInitials, vendorAccount, productCode. Possible match outcomes:
| Outcome | Fallback level |
|---|---|
| All three columns match a row | No fallback — the matched row wins |
customerInitials matches some rows, vendorAccount narrows further, productCode doesn't match → no row | Level 2 fires |
customerInitials matches some rows, vendorAccount doesn't match | Level 1 fires |
customerInitials doesn't match anything | Level 0 fires |
Levels you don't configure fall through to the next level. If only Level 0 is configured, every non-match scenario gets that fallback. If only Level 2 is configured, scenarios where the third column was the issue use Level 2; scenarios where the first or second column failed get the silent no-op.
When to configure fallbacks
Fallbacks earn their place when:
- A default value is genuinely better than nothing. If a vendor isn't in your master, defaulting to a "Unknown vendor" record might prevent downstream chaos.
- The system shouldn't silently move on. A default value forces a visible indication that the lookup didn't fully match — the reviewer sees the default and knows to investigate.
- You're handling a known gap. A new tenant in a discriminator-pattern project hasn't had their data uploaded yet; a fallback covers the gap until the data lands.
Don't configure fallbacks reflexively. Many projects work fine with silent no-ops because the reviewer notices the missing values and acts (adds the missing row, clicks Reset data, etc.). Fallbacks can hide real data-availability problems.
Designing fallback rows
A fallback row is just an entity row you pick from the existing data. There's no special "this is a fallback" flag. Common patterns:
- Sentinel rows. Add a row to the entity specifically as a fallback — e.g., a Vendors row with
vendorId="UNKNOWN",vendorName="—Unknown Vendor—", and other columns left empty. - Catch-all rows. Add a row with broad matching values (e.g.,
customerInitials=""to match any customer) and pick it as the fallback. - Default-default rows. For a discriminator pattern, add a row per discriminator value with conservative defaults.
The empty-cell rule applies to fallback rows too — empty cells in the fallback row don't overwrite document fields. So a fallback row with vendorName="Unknown" and glAccount=(empty) writes the name and leaves the GL account alone.
What fallbacks don't do
A few constraints:
- Fallbacks don't fire on partial matches. If three rows match the Context AND, the engine picks the top one — fallbacks don't override "top match" logic.
- Fallbacks don't combine with other fallbacks. Each level is independent; you can't compose them.
- Fallbacks don't transform values. They just substitute the row. Whatever's in the row writes to the document fields.
What's next
- Cascade behavior — what happens in the rest of the cascade after a step runs.
- The empty-cell rule — important for understanding what fallback rows write.
- Fallback records in definitions — the configuration UI side.