A configured expression must be valid for its engine at save
An expression that cannot parse can only fail once the workflow is already running. Catching it at save turns a runtime failure into an editing error.
The rule
- Every expression carried in a node's configuration must pass its engine's validation when the workflow is saved.
- A failure is refused with
R11_EXPRESSION_INVALID, and the error names the node, the config key, the engine and the expression. - The expressions checked are the ones the node types declare as expression-bearing: a data extractor's
path, thevalueof each dynamic output of a data mapper, the sources of a data shaper'smapping(with reserved source values such as a literal or a now marker skipped, and nested_sourceand_eachsources walked), and a prompt template'stemplate. - An empty expression is always valid.
- An expression naming an engine the implementation does not know is not reported here; R6's
enumcheck on the engine key owns that error. - An engine whose validation raises rather than returning a verdict is treated as a rejection, yielding one
R11_EXPRESSION_INVALIDfor that expression; the failure is never propagated, so a misbehaving engine still produces a refusal against the workflow and never a server error.
What it means
Not every value written where an expression is expected is checked as one. A
Data Shaper's mapping accepts reserved sentinel values — a literal, a "now"
marker — that the runtime never evaluates as an expression at all, so
checking their syntax would only invent errors against data that is already
well formed. The walk does still reach into a mapping entry that iterates a
nested source: a _source/_each pair inside an entry is not itself a
sentinel, and the runtime does evaluate it, so its expression is checked
exactly as a top-level one would be. An engine that raises rather than
returning a verdict is treated the same as one that returns a rejection: the
save still fails with the same per-expression error, never with the failure
of the engine itself.
Example
A mapping with two sentinel values and one nested, iterated source whose expression cannot parse:
{
"literal": "_LITERAL:not [a path",
"stamp": "_NOW_",
"items": { "_source": "[unclosed", "_each": { "name": "[title]" } }
}Neither sentinel is checked; the mapping is refused for the unparseable nested source alone.
Why
Recorded under OPEN-3.