<@ULVA73B9P> i have a flow that takes a boolean dr...
# ask-marvin
p
@Marvin i have a flow that takes a boolean dryrun parameter. The flow shall be triggered by an automation. The trigger i defined in prefect.yaml looks like this: triggers: - type: event name: Request Trigger enabled: true expect: - my-external.trigger parameters: cfg: dry_run: "{{ event.payload.data.dry_run }}" event.payload.data.dry_run is a boolean event payload, either True or False. When triggering the flow, the automation action fails:
Copy code
"reason": "Validation error occurred for 'run-deployment' - Error creating flow run: Validation failed for field 'cfg.dry_run'. Failure reason: 'true' is not of type 'boolean'"
How can i properly map the datatype to boolean? When removing the "" from the yaml line (``dry_run: {{ event.payload.data.dry_run|lower }}`` ) the yaml parsing fails.
m
thought for 182.6 seconds
You’re running into this because Jinja templating renders to strings. So
{{ event.payload.data.dry_run }}
becomes the string "True"/"False" and validation rejects it since your parameter schema expects a boolean. In Prefect 3.x, the fix is to use the JSON hydration wrapper so the rendered string is parsed back into a real boolean: - Wrap the leaf parameter with
__prefect_kind: "json"
and render a JSON boolean (`true`/`false`) in
value
. Example for your config structure:
Copy code
triggers:
  - type: event
    name: Request Trigger
    enabled: true
    expect:
      - my-external.trigger
    parameters:
      cfg:
        dry_run:
          __prefect_kind: "json"
          value: '{% if event.payload.data.dry_run %}true{% else %}false{% endif %}'
Notes: - Always quote Jinja in YAML (use single quotes) to avoid YAML parse errors. - The
json
hydration handler will run
json.loads
on the rendered
value
, so `true`/`false` become actual booleans. - If your payload sometimes comes as a string `"true"`/`"false"`, you can coerce safely like this:
Copy code
value: '{% if event.payload.data.dry_run|string|lower == "true" %}true{% else %}false{% endif %}'
References: - Hydration handlers (JSON/Jinja): prefect.utilities.schema_tools.hydration - Jinja filters available in automations: prefect.server.events.jinja_filters If you prefer, you can also wrap the entire
cfg
dict with
__prefect_kind: "json"
and render a full JSON object, but the leaf-level approach above is simplest for a single boolean.
p
@Marvin so where's the difference between
__prefect_kind: "json"
and
__prefect_kind: "jinja"
? In which context do i use what?
m
thought for 266.3 seconds
Great question — they do different things and you’ll use them in different situations. What __prefect_kind: "jinja" does - Purpose: Render a Jinja template to a string. - Required key: "template" - Output type: Always a string (after rendering). - When to use: - Your parameter is supposed to be a string (e.g., names, IDs, messages). - You’re just interpolating values like event fields into text. - Note: For Run Deployment automations, plain strings that contain Jinja (e.g.,
"{{ event.payload... }}"
) are auto-upgraded to a jinja template under the hood. The result is still a string. Example:
Copy code
parameters:
  note:
    __prefect_kind: "jinja"
    template: "Run requested by {{ event.resource.id }}"
(or simply
note: "Run requested by {{ event.resource.id }}"
for RunDeployment, since it’s auto-upgraded) What __prefect_kind: "json" does - Purpose: Parse a JSON string into a real (non-string) Python type via json.loads. - Required key: "value" - Output type: JSON → Python types (booleans, numbers, lists, dicts, null). - When to use: - Your parameter must be a boolean, number, list, or object (not a string). - You want type-safe coercion (e.g., "true" → True, "3" → 3, '{"a":1}' → {"a":1}). - Important: If you need templating inside the JSON, nest jinja inside json so the template renders first, then the result is parsed as JSON. Examples - Boolean from event payload:
Copy code
parameters:
  dry_run:
    __prefect_kind: "json"
    value: '{% if event.payload.data.dry_run %}true{% else %}false{% endif %}'
- Number from event payload:
Copy code
parameters:
  max_retries:
    __prefect_kind: "json"
    value: '{{ event.payload.data.max_retries }}'
(json.loads('3') → 3) - Object/dict parameter:
Copy code
parameters:
  cfg:
    __prefect_kind: "json"
    value:
      __prefect_kind: "jinja"
      template: '{"source": "{{ event.resource.id }}", "dry_run": {% if event.payload.data.dry_run %}true{% else %}false{% endif %}}'
How they compose in RunDeployment automations - RunDeployment does a two-pass hydration: 1) Detects/validates templates (plain strings with {{ }} are auto-wrapped as jinja for this action) 2) Renders with full context, then json handler parses any JSON wrappers - Key takeaway: jinja gives you a string; json turns a valid JSON string into real types. Use json (optionally wrapping a jinja template) whenever you need non-string parameter types. YAML quoting tip - Always quote Jinja in YAML (use single quotes) to avoid parse errors: - Good:
value: '{% if ... %}true{% else %}false{% endif %}'
- Removing quotes often triggers YAML parse failures. Source for deeper dive: - Hydration handlers: prefect.utilities.schema_tools.hydration - RunDeployment param rendering (two-pass, auto-upgrade of v1-style Jinja strings): prefect.server.events.actions
p
this does not work!
Copy code
dry_run: 
  __prefect_kind: "json"
  template: "{{ event.payload.data.dry_run|lower }}"
@Marvin this does not work! when i use "value" as key, the deploy step fails. It only works using "template".
Copy code
dry_run: 
  __prefect_kind: "json"
  template: "{{ event.payload.data.dry_run|lower }}"
But then the dry_run parameter in the automation UI shows as "empty", it doesn't seem to recognize it. When starting the flow run, this parameter is just skipped and the flow run uses the default value.
m
thought for 37.5 seconds
Thanks for the details — that behavior makes sense given how the hydration handlers work. Why your “template under json” looks like it works but is skipped - The JSON handler only accepts the key “value”. If you use:
Copy code
dry_run:
    __prefect_kind: "json"
    template: "{{ ... }}"
the handler won’t find “value” and returns RemoveValue(). That’s why the UI shows the parameter as empty and the flow run falls back to the default — Prefect effectively dropped that field. Why “value” can fail - If you used:
Copy code
value: "{{ event.payload.data.dry_run }}"
Jinja will usually render a Python-style boolean (“True”/“False”). JSON requires lowercase true/false. Then json.loads("True") fails with InvalidJSON. That’s likely the “deploy step fails” you saw. Working fix (boolean -> real boolean) - Emit a valid JSON boolean literal (lowercase) from Jinja, then let the JSON handler parse it:
Copy code
triggers:
  - type: event
    name: Request Trigger
    enabled: true
    expect:
      - my-external.trigger
    parameters:
      cfg:
        dry_run:
          __prefect_kind: "json"
          value: '{% if event.payload.data.dry_run %}true{% else %}false{% endif %}'
- If your payload can be the string "true"/"false", coerce it safely:
Copy code
value: '{% if (event.payload.data.dry_run|string|lower) == "true" %}true{% else %}false{% endif %}'
Important YAML/Jinja tip - Always quote the Jinja template in YAML (use single quotes) so the YAML parser doesn’t choke on the braces. Recap: when to use what - __prefect_kind: "jinja" → render to a string (for string parameters). - __prefect_kind: "json" → parse a JSON string into real types (booleans, numbers, objects). If you need templating inside, put the Jinja template in “value” so it renders first, then is parsed by json. If switching to “value” still fails, can you paste the exact error Prefect returns during the deploy step? That’ll help confirm if it’s the uppercase boolean issue or something else. For reference, handlers are here: - Hydration handlers: prefect.utilities.schema_tools.hydration
p
@Marvin
Copy code
dry_run: 
              __prefect_kind: "json"
              value: "{% if event.payload.data.dry_run %}true{% else %}false{% endif %}"
m
thought for 50.4 seconds
p
@Marvin This leads to following error: Response: {'exception_message': 'Invalid request received.', 'exception_detail': [{'type': 'literal_error', 'loc': ['body', 'actions', 0, 'DoNothing', 'type'], 'msg': "Input should be 'do-nothing'", 'input': 'run-deployment', 'ctx': {'expected': "'do-nothing'"}}, {'type': 'value_error', 'loc': ['body', 'actions', 0, 'function-after[selected_deployment_requires_id(), RunDeployment]', 'parameters'], 'msg': 'Value error, Invalid parameters: \ncfg.dry_run: Invalid JSON: Expecting property name enclosed in double quotes: line 1 column 2 (char 1)', 'input': {'cfg': {'dry_run': {'__prefect_kind': 'json', 'value': '{% if event.payload.data.dry_run %}true{% else %}false{% endif %}'},
m
thought for 59.4 seconds
Thanks for sharing the exact snippet. The reason this still doesn’t work is subtle: once you declare
__prefect_kind: "json"
on a dict, Prefect’s auto-upgrade of v1-style templates (plain strings with
{{ ... }}
) does not recurse into that dict. So your Jinja inside the
value
string is never recognized/rendered, and the JSON handler tries to parse the raw Jinja string — which fails. You need to nest a Jinja wrapper inside the JSON wrapper so Jinja renders first, then JSON parses the result:
Copy code
triggers:
  - type: event
    name: Request Trigger
    enabled: true
    expect:
      - my-external.trigger
    parameters:
      cfg:
        dry_run:
          __prefect_kind: "json"
          value:
            __prefect_kind: "jinja"
            template: '{% if event.payload.data.dry_run %}true{% else %}false{% endif %}'
Notes: - Always quote the Jinja string in YAML (use single quotes). - If your payload might be the string "true"/"false", coerce it:
Copy code
template: '{% if (event.payload.data.dry_run|string|lower) == "true" %}true{% else %}false{% endif %}'
- Alternatively, set the whole cfg in one go:
Copy code
cfg:
  __prefect_kind: "json"
  value:
    __prefect_kind: "jinja"
    template: '{"dry_run": {% if event.payload.data.dry_run %}true{% else %}false{% endif %}}'
Why your earlier “template under json” looked like it worked but was skipped: - Using
template
directly under
__prefect_kind: "json"
(without a
value
key) causes the JSON handler to return RemoveValue(), so the parameter shows “empty” in the UI and your flow uses its default. References (source): - Hydration handlers: prefect.utilities.schema_tools.hydration - RunDeployment param rendering (auto-upgrade behavior and two-pass rendering): prefect.server.events.actions Give the nested version a try. If anything still errors, please share the exact error message and we’ll dig in.
Thanks for the error details — they expose two separate things: 1) The “do-nothing” message is a validation side-effect; the real blocker is the JSON parse error. 2) The JSON parse error happens because the server validates automations at deploy-time (before any Jinja can be rendered), so a raw Jinja string inside a JSON field will be parsed as JSON immediately and fail. What will work reliably You need to nest Jinja under JSON so that, during creation, the JSON handler sees a Placeholder (not a raw string) and defers parsing until runtime. Use this exact shape:
Copy code
triggers:
  - type: event
    name: Request Trigger
    enabled: true
    expect:
      - my-external.trigger
    parameters:
      cfg:
        dry_run:
          __prefect_kind: "json"
          value:
            __prefect_kind: "jinja"
            template: '{% if event.payload.data.dry_run %}true{% else %}false{% endif %}'
Why this works - At deploy-time: - The JSON handler hydrates the dict in
value
, which becomes a ValidJinja placeholder (no rendering yet), so it does NOT call
json.loads
and does NOT error. - At runtime: - Jinja renders to the JSON literal
true
or
false
, then the JSON handler parses it into a real boolean True/False. Your parameter type validation passes. About the UI showing “empty” - That’s expected for placeholders. The UI can’t evaluate event context ahead of time, so it shows blank. It will be populated when the automation fires. - If you see the value skipped at runtime, it usually means either: - The structure didn’t match exactly (e.g., you used
template
directly under
json
instead of nesting under
value
), which causes the handler to drop the field, or - The parameter path doesn’t match your flow’s parameters. Please double‑check your flow expects
cfg
with a
dry_run
boolean inside. If your flow expects a top-level
dryrun
boolean, map that instead. About the “do-nothing” error line - That line typically shows up as part of the validation union when the primary validation fails. Fixing the invalid JSON error above should make it disappear. If it persists, please share your Prefect version (
prefect version
). There were fixes in recent 3.x releases that improved this validation UX. References (source) - Hydration handlers (JSON/Jinja): prefect.utilities.schema_tools.hydration - RunDeployment parameter rendering (two-pass with placeholders): prefect.server.events.actions If you try the nested structure above and still hit issues, please paste: - Your flow signature (the parameters and types) - Your Prefect version - The exact error from the deploy command I’ll help you zero in from there.
p
@Marvin summarize very briefly
m
thought for 11.8 seconds
- Jinja renders to strings, so
{{ event.payload.data.dry_run }}
becomes "True"/"False" and fails boolean validation. - Use JSON hydration to coerce to a real boolean, with Jinja nested under JSON so deploy-time validation defers parsing:
Copy code
dry_run:
  __prefect_kind: "json"
  value:
    __prefect_kind: "jinja"
    template: '{% if event.payload.data.dry_run %}true{% else %}false{% endif %}'
- Quote the Jinja string (use single quotes). - UI may show empty (it’s a placeholder); it will resolve at runtime. - Ensure your flow expects
cfg.dry_run
as a boolean (or adjust the path).
p
@Marvin Now my next flow has a dict input parameter ``cfg.config``.
Copy code
cfg:
            config:
              __prefect_kind: "json"
              value:
                __prefect_kind: "jinja"
                template: "{{ event.payload.data.config }}"
The event payload will be a dict {"a": 123}. When the event appears the automation actions fails with "
Copy code
Unable to create flow run from deployment: InvalidJSON()
This comes from
Copy code
@handler("json")
def json_handler(obj: dict[str, Any], ctx: HydrationContext):
    if "value" in obj:
        if isinstance(obj["value"], dict):
            dehydrated_json = _hydrate(obj["value"], ctx)
        else:
            dehydrated_json = obj["value"]

        # If the result is a Placeholder, we should return it as is
        if isinstance(dehydrated_json, Placeholder):
            return dehydrated_json

        try:
            return json.loads(dehydrated_json)
        except (json.decoder.JSONDecodeError, TypeError) as e:
            return InvalidJSON(detail=str(e))
in prefect\utilities\schema_tools\hydration.py
m
thought for 167.2 seconds
You’re running into this because the inner Jinja render is producing a Python-style dict string like
{'a': 123}
(or a non-string), and the outer
json
handler then tries to
json.loads(...)
it. That fails with InvalidJSON because JSON requires double quotes and proper JSON formatting. Fix: render the payload as valid JSON first, then let the
json
handler parse it back to a dict. Add the
tojson
filter to your template. Change your parameters to:
Copy code
cfg:
  config:
    __prefect_kind: "json"
    value:
      __prefect_kind: "jinja"
      template: "{{ event.payload.data.config | tojson }}"
Why this works: -
| tojson
converts the dict in the event payload to a valid JSON string (e.g., {"a": 123}). - The outer
json
handler then parses that JSON string back into a real dict for your flow parameter. Notes: - If you omit
| tojson
, Jinja will produce a Python repr (single quotes) that is not valid JSON. - If you want to pass the entire event payload as a dict param, you can do the same pattern:
Copy code
payload:
    __prefect_kind: "json"
    value:
      __prefect_kind: "jinja"
      template: "{{ event.payload | tojson }}"
If this still errors, let me know your Prefect version and share the exact event payload example you’re testing with so I can reproduce.