Diagnostics
This page explains how FLASH reports problems (parse errors, definition errors, evaluation/validation issues) and how severity thresholds affect whether an issue throws, logs, or is returned in a diagnostic report.
If you’re still learning rule forms and indentation, start with Syntax. If you’re trying to predict append/override or input fan-out behavior, see Evaluation model.
Diagnostic sources
Diagnostics come from multiple stages of the FLASH pipeline. Knowing which stage you’re in usually tells you what to change.
Parse-time errors (syntax and indentation)
Parse-time errors happen when FLASH can’t be parsed into declarations and rules.
- These errors prevent evaluation.
- They tend to point at a specific line/indentation level or a malformed rule form.
Examples of parse-time issues include missing declarations (like InstanceOf:), malformed rule lines, or indentation that isn’t in 2-space increments.
Definition-resolution errors (FHIR structure navigation)
After parsing, FLASH resolves the output type and every rule path against the active FHIR definition set.
- If no Structure Navigator (or definition set) is available, FLASH cannot perform conformance-driven resolution.
- If a path segment doesn’t exist on the resolved type, the block fails before any rule writes happen.
This category includes “unknown type/profile” and “invalid element path” failures.
Evaluation and validation diagnostics (type checks, regexes, cardinality, bindings)
Once a rule path resolves, FLASH evaluates the right-hand-side expression and applies a write. Conformance-aware shaping and validation can emit additional diagnostics, such as:
- assigning a primitive where a complex object is required
- a primitive value that fails a FHIR regex constraint
- required (min=1) elements that were never populated
Many validation diagnostics are in the F5xxx code family and are controlled by the threshold model described below.
User-raised diagnostics and logs
Some diagnostics are intentionally raised by mappings (for example, $warn, $info, and $trace functions). These are still subject to logging/collection thresholds, and are easiest to see via verbose evaluation.
Threshold model
FLASH uses numeric severity to decide what happens to a diagnostic.
- Lower numbers are more critical.
- Threshold comparisons are exclusive: an issue triggers an action when
severity < threshold.
Severity bands
For policy-governed FLASH validation diagnostics (F5xxx), the severity is derived from the code itself:
F5xyyhas severityxy(for example,F5110has severity11).
For everything else (including JSONata codes like S0201 and non-F5 FLASH codes like F1006), the policy treats the severity as fatal.
Common level boundaries:
0= fatal10= invalid20= error30= warning40= notice50= info60= debug
Thresholds
The evaluation environment provides four thresholds:
throwLevel: issues withseverity < throwLevelare treated as “throwing” in non-verbose evaluation.logLevel: issues withseverity < logLevelare emitted to the runtime logger.collectLevel: issues withseverity < collectLevelare collected into the per-evaluation diagnostic bag.validationLevel: controls whether certain validations run and whether a diagnostic is enforceable.- When an issue’s severity is not within the validation band, the issue is treated as inhibited: it can be collected, but it does not log or throw.
Default thresholds (when your runtime doesn’t override them):
throwLevel = 30(fatal/invalid/error throw; warnings do not)logLevel = 40(fatal/invalid/error/warning log)collectLevel = 70(everything is eligible to be collected)validationLevel = 30(fatal/invalid/error validations are enforced)
Raising a threshold makes behavior stricter by including less-critical bands (for example, setting throwLevel to 40 makes warnings eligible to throw).
evaluate vs evaluateVerbose
Most integrations expose two evaluation modes:
- Non-verbose evaluation (
evaluate) returns a result on success, and may throw when an enforceable diagnostic is in the throwing band. - Verbose evaluation (
evaluateVerbose) returns a report that includes both the result (when available) and a diagnostics bag; it does not throw for handled FLASH/JSONata issues.
Verbose evaluation is the fastest way to debug because you can see:
- which diagnostics were produced
- which ones were collected (affected by
collectLevel) - whether any collected issues are in the throwing band (affected by
throwLevel)
Representative codes
This section lists a small set of common diagnostics. It is not a complete code catalog.
F1000 — No Structure Navigator provided
Meaning: your expression contains FLASH blocks, but evaluation has no FHIR Structure Navigator / definition set available, so FLASH can’t resolve types and paths.
Typical causes:
- the runtime is executing “JSONata only” without a configured FHIR navigator
- the navigator was not wired into the evaluation environment
What to do:
- Ensure your runtime config provides a Structure Navigator loaded with the FHIR definitions your mapping expects.
F1006 — Malformed FLASH rule
Meaning: a line inside a FLASH block does not match any valid rule form.
Example (missing *):
InstanceOf: Patient
active = true
Fix:
InstanceOf: Patient
* active = true
F1007 — Missing InstanceOf:
Meaning: a FLASH block is missing the required InstanceOf: declaration.
Example:
* active = true
Fix:
InstanceOf: Patient
* active = true
F2002 — Invalid element path
Meaning: the rule path contains a segment that does not exist on the resolved FHIR type/profile.
Example:
InstanceOf: Patient
* madeUpElement = "nope"
What to do:
- Check that every path segment exists on the target type.
- If you intended a choice-type element, select a concrete JSON name (for example, write
valueStringorvalueQuantityrather thanvalue[x]). - If you intended a repeating element, confirm you’re using a nested/context rule or the correct element name for the repeating container.
F5104 — Complex object expected, primitive assigned
Meaning: the target element’s type is complex (object-shaped), but the assigned value is a primitive.
Example (assigning a string to name, which is a complex HumanName):
InstanceOf: Patient
* name = "ExampleCare"
Fix by targeting a primitive child:
InstanceOf: Patient
* name
* family = "ExampleCare"
* given = "Avery"
F5110 — Primitive fails a FHIR regex constraint
Meaning: a primitive string value does not match the regex constraint for the target element’s type.
Example (FHIR Resource.id has a strict pattern; spaces are not allowed):
InstanceOf: Patient
* id = "ExampleCare Patient 001"
What to do:
- Use a valid id token (for example,
ExampleCare-Patient-001). - If the value comes from input, normalize it in JSONata before assignment.
F5130 — Mandatory element missing
Meaning: the resolved definition says an element has a minimum cardinality (min) greater than 0, but no value was provided.
Example (an Observation is missing required elements like status and code):
InstanceOf: Observation
* valueString = "ExampleCare note"
Fix by populating the required fields:
InstanceOf: Observation
* status = "final"
* code.text = "ExampleCare note"
* valueString = "ExampleCare note"
Debugging workflows
“I get undefined result”
- Check for fatal/parse/definition diagnostics first (these prevent meaningful output).
- If you can, run the same mapping through verbose evaluation and inspect the collected diagnostics.
- If there are no critical diagnostics, look for rules whose right-hand side evaluates to
undefined(those rules perform no write).
When the issue is “no writes happened”, start with one simple assignment that is known-good for the target type (for example, * active = true on Patient) to confirm that the block is being evaluated.
“My rule doesn’t write anything”
Common causes:
- The path doesn’t resolve (look for definition-resolution codes like
F2002). - The expression evaluates to
undefinedfor the current input context (no write occurs). - The value is the wrong shape for the target type (look for
F51xxdiagnostics likeF5104).
If the rule is inside an input-context fan-out block (* (<expr>).<path>), confirm what <expr> evaluates to (object vs array vs undefined).
“It throws only in one environment”
When a mapping behaves differently between environments, the usual causes are:
- Different FHIR definition sets (different packages/profiles loaded by the navigator).
- Different threshold configuration (for example, a higher
throwLevelorvalidationLevelmakes more issues enforceable). - Different runtime integrations (for example, one environment uses verbose evaluation and the other uses non-verbose evaluation).
To isolate the difference:
- Run verbose evaluation in both environments.
- Ensure
collectLevelis high enough to include any issue that could be throwing (a common safe choice iscollectLevel >= throwLevel). - Compare diagnostics by code (the code is the stable identifier; messages can include contextual details).