Skip to content

fix: render trigger action parameters as plain strings (closes #9742)#9747

Merged
polmichel merged 7 commits into
stablefrom
ai-bug-pipeline-9742-webhook-render
Jun 29, 2026
Merged

fix: render trigger action parameters as plain strings (closes #9742)#9747
polmichel merged 7 commits into
stablefrom
ai-bug-pipeline-9742-webhook-render

Conversation

@polmichel

@polmichel polmichel commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

Why

Webhook automations crashed at Prefect parameter-render time, so no HTTP request was ever sent. Trigger definitions emitted single-expression action parameters as bare Jinja strings; RunDeployment._upgrade_v1_templates appends | tojson to them, and json.dumps() fails on values that are not JSON-native (a UUID, a datetime) or that resolve to an absent resource key (ChainableUndefined).

The | tojson coercion was introduced in Prefect 3.6.24 (PrefectHQ/prefect#21231); it is absent in 3.6.23 and earlier. The crash surfaced once we moved to prefect==3.7.5 (Infrahub 1.10.0, up from 3.6.13). Webhooks were the visible victim because custom webhooks default to event_type="all", so they receive branch-less events (e.g. account events) where the branch key is missing. The same bare-string pattern existed in seven other trigger builders; they did not crash only because their narrow event triggers always carry the referenced keys.

Closes #9742

What changed

  • Behavioral: Webhooks now fire for every triggering event, including events with no branch. Scalars are delivered as strings; a missing branch resolves to an empty string.
  • Implementation: Add a shared jinja_parameter() helper that wraps a template as an explicit {"__prefect_kind": "jinja", "template": ...} parameter — Prefect's documented way to mark a templated parameter, which Prefect leaves untouched by the tojson upgrade. Apply it to every single-expression action parameter across all trigger builders.
  • Scope note: The webhook builder is the live fix; the other seven are preventative hardening against the same failure. String-valued params render identically to before, so existing behavior is preserved.

How to review

Start with backend/infrahub/trigger/models.py (jinja_parameter helper), then backend/infrahub/webhook/models.py (the fix). The remaining builder edits are mechanical applications of the same helper. Commits are atomic: helper → webhook fix+tests → systemic hardening → changelog.

How to test

# Unit: builder output renders to strings through Prefect's render path
uv run pytest backend/tests/unit/webhook/test_trigger_definition_builder.py::TestWebhookParameterRender -v
# Live Prefect: a branch-less event fires the automation and dispatches webhook-process
uv run pytest backend/tests/functional/webhook/test_render.py -v

Manual end-to-end verification (full Docker stack)

Verified on a live dev.start stack (real server, Prefect, two task-workers) with a custom webhook (event_type=all) pointing at a local capture server:

  • Node event (infrahub.node.created): webhook delivered; payload carried the previously-crashing values as strings — "id" (UUID) and "occured_at" (datetime).
  • Branch-less event (infrahub.account.logged_in, whose resource has no infrahub.branch.name): webhook also delivered — the ChainableUndefined path.
  • Negative control (fix reverted in the running app): the same node event produced no delivery; the Prefect server logged TypeError: Object of type UUID is not JSON serializableAction failed: 'Unable to create flow run from deployment: InvalidJinja()', exactly the reported failure.

Checklist

  • Tests added/updated
  • Changelog entry added
  • External docs updated — N/A
  • Internal .md docs updated — N/A
  • I have reviewed AI generated content

@github-actions github-actions Bot added the group/backend Issue related to the backend (API Server, Git Agent) label Jun 29, 2026

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No issues found across 1 file

Confidence score: 5/5

  • Automated review surfaced no issues in the provided summaries.
  • No files require special attention.

Shadow auto-approve: would auto-approve. Adds a failing unit test reproducing a Jinja template rendering bug in webhook automation. No source code changes, minimal risk.

Re-trigger cubic

@codspeed-hq

codspeed-hq Bot commented Jun 29, 2026

Copy link
Copy Markdown

Merging this PR will not alter performance

✅ 12 untouched benchmarks


Comparing ai-bug-pipeline-9742-webhook-render (939f112) with stable (afcea68)

Open in CodSpeed

@opsmill-bug-pipeline

This comment was marked as outdated.

@polmichel polmichel changed the title test: failing test for #9742 -- webhook automation Prefect parameter render fix: webhook automations crash during Prefect parameter render (closes #9742) Jun 29, 2026

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

0 issues found across 2 files (changes from recent commits).

Shadow auto-approve: would auto-approve. Fixes webhook parameter rendering for non-JSON-native types by wrapping scalars as explicit jinja kind dicts. Changes are isolated to webhook trigger builder, well-tested, and no broader system impact.

Re-trigger cubic

@opsmill-bug-pipeline

This comment was marked as outdated.

polmichel and others added 3 commits June 29, 2026 18:05
Provide a single helper that marks a single-expression Jinja action parameter
as an explicit jinja kind, so Prefect renders it as a plain string instead of
JSON-serializing the rendered value.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
Emit the webhook trigger's scalar action parameters via the jinja_parameter
helper so a firing automation renders the event id, occurred time, and an
absent branch resource as strings, dispatching the webhook-process deployment
for every triggering event including events with no associated branch.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
Apply the jinja_parameter helper to the remaining trigger builders so their
single-expression action parameters render as plain strings, hardening them
against the same serialization failure when an event lacks a referenced
resource key.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
@polmichel polmichel force-pushed the ai-bug-pipeline-9742-webhook-render branch from b0b71a5 to b317fcf Compare June 29, 2026 16:20
@polmichel polmichel changed the title fix: webhook automations crash during Prefect parameter render (closes #9742) fix: render trigger action parameters as plain strings (closes #9742) Jun 29, 2026
@opsmill-bug-pipeline

This comment was marked as outdated.

@polmichel polmichel force-pushed the ai-bug-pipeline-9742-webhook-render branch from b317fcf to aaf911b Compare June 29, 2026 16:51
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
@polmichel polmichel force-pushed the ai-bug-pipeline-9742-webhook-render branch from aaf911b to 4d837c2 Compare June 29, 2026 16:55
@opsmill-bug-pipeline

Copy link
Copy Markdown

AGENT_REVIEW_VERDICT: FIX_APPROVED

Overall verdict: APPROVED

This re-review confirms the fix is complete and both non-blocking items from fix-2 are now resolved. No new commits were pushed since fix-2 (HEAD is still 4d837c2); the PR-body rewrite is what re-triggered review — and that rewrite, plus a new housekeeping changelog, closes out the remaining feedback. The code, tests, and docs are now mutually consistent. Ready for a human to merge.

A. Correctness — solid

  • Root cause fixed via jinja_parameter() (backend/infrahub/trigger/models.py:213-223), which emits {"__prefect_kind": "jinja", "template": "{{ ... }}"}. Prefect's _upgrade_v1_templates (>=3.6.24) leaves __prefect_kind-tagged dicts untouched, so no | tojson is appended and the value renders as a plain string — the same mechanism already used for event_payload.
  • The four webhook scalars (backend/infrahub/webhook/models.py:85-88) are fixed, and the branch-less path is safe end-to-end: the undefined branch renders to "", and webhook_process(..., branch_name: str | None = None) guards with if branch_name:, so "" behaves like None.
  • Completeness re-verified independently. I grepped all of backend/infrahub for bare single-expression "{{ ... }}" parameter strings: the only remaining matches are schema display_label definitions (core/key_value.py, core/permission.py), which are not Prefect action parameters and are outside this bug's path. Every single-expression action parameter across all 8 builders (webhook, computed_attribute, profiles, hfid, display_labels, actions, schema, branch) is now wrapped.

B. Code quality — good

  • The helper is small, single-responsibility, well-placed, and its docstring names the upstream symbol + version (RunDeployment._upgrade_v1_templates, >=3.6.24) — the permitted "upstream library workaround" exception in code-doc-style, not a violation.
  • The expansion to all builders is mechanical and uniform. No unnecessary refactoring.

C. Documentation alignment — now resolved

  • The fix-2 blocking-adjacent item is fixed. The PR body no longer claims the change is webhook-only or that profiles is "out of scope." It now accurately describes the jinja_parameter() helper and lists all eight builders, with profiles explicitly included. Body and diff are consistent.
  • fix-2 suggestion 2 addressed: the new changelog/9742.housekeeping.md documents that all event-driven automations are now rendered through the shared helper — exactly the broader-scope note that was requested. changelog/9742.fixed.md remains accurate for the webhook-facing behavior.
  • No public API / schema / dependency change. Consistent with the PR's N/A markings.

D. Test quality — good

  • fix-1 blocker stays resolved. backend/tests/component/webhook/test_trigger_definition_builder.py:60-63 and :106-109 assert the __prefect_kind dict shape; backend/tests/component/profiles/test_models.py:105-112 likewise.
  • No regression reintroduced. I swept backend/tests for the old bare-string parameter assertions: only the webhook and profiles component files reference these parameters, and both are updated. backend/tests/component/actions/test_gather.py:380,401 asserts only generator_definition_id (unchanged), so the node_ids list-of-jinja change does not break it. No test for computed_attribute, display_labels, hfid, branch, or schema asserts the old format — CI stays green.
  • The unit test (backend/tests/unit/webhook/test_trigger_definition_builder.py) faithfully mirrors Prefect's server-side render (_upgrade_v1_templates + hydrate(..., raise_on_error=True)), covers all three non-JSON-native cases (UUID, datetime, undefined branch), uses copy.deepcopy correctly, and asserts the fixed behavior.
  • The functional test (backend/tests/functional/webhook/test_render.py) drives a genuine branch-less event through the live Prefect server and asserts the deployment actually runs — strong end-to-end coverage of the path the unit test can only approximate.

Suggestions (non-blocking)

  1. The functional test posts via the private prefect_client._client.post("/events", ...). That is the pragmatic way to inject a raw event server-side, but it couples the test to a Prefect internal. A one-line comment noting why the private client is used would help the next reader on a Prefect upgrade. Optional.

Recommended next steps

  1. Nothing blocking. Merge when ready.
  2. (Optional) Add the private-client rationale comment in test_render.py per suggestion 1.

AGENT_REVIEW_ITERATION: fix-3

Generated by Bug reviewer agent for #9747 · 126.4 AIC · ⌖ 28.8 AIC · ⊞ 11.5K ·

Add a knowledge-doc section describing how trigger action parameters are
rendered server-side by Prefect and why single-expression values must be
emitted via the jinja_parameter helper, and correct a stale builder
reference in the webhook doc.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

0 issues found across 2 files (changes from recent commits).

Shadow auto-approve: would auto-approve. Adds a jinja_parameter helper and applies it to all event-driven action parameters to prevent Prefect serialization failures. Mechanical changes across multiple trigger builders with tests.

Re-trigger cubic

@opsmill-bug-pipeline opsmill-bug-pipeline Bot mentioned this pull request Jun 29, 2026
@polmichel polmichel marked this pull request as ready for review June 29, 2026 17:53
@polmichel polmichel requested a review from a team as a code owner June 29, 2026 17:53
event="infrahub.node.created",
payload={"data": {"node_id": "abc"}, "context": {}},
resource=Resource({"prefect.resource.id": "infrahub.account.xyz"}),
)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see a datetime in here, which is what the comment describes, but perhaps it happens behind-the-scenes

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's defaulted to today's date, but you're right, explicit is better than implicit. And a fixed date is better than a moving one from a test perspective

rendered = hydrate(
parameters,
HydrationContext(raise_on_error=True, render_jinja=True, jinja_context={"event": event}),
)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this looks like we are testing Prefect's code, which we don't really have control over. if this test covers something that the functional test does not, then it might be worth keeping. otherwise it seems like it will be too brittle to be valuable

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point. This is what was executed behind the scenes during parameters serialization. Since we have the component test covering this, I think we can remove it. Useful at the review step but not further.

polmichel and others added 2 commits June 29, 2026 22:40
Pass the event's occurred time explicitly so the datetime value the comment
describes is visible in the test rather than relying on the model default.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
The unit test replayed Prefect's private parameter-render path to assert
template output. The emit-contract is covered structurally by the component
test and the end-to-end behavior by the live functional test, so remove the
test that coupled to Prefect internals.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

0 issues found across 2 files (changes from recent commits).

Shadow auto-approve: would auto-approve. Bug fix: wraps Jinja template parameters to prevent Prefect serialization failure. Systematic, low-risk change across trigger definitions and tests.

Re-trigger cubic

@opsmill-bug-pipeline

This comment was marked as off-topic.

@opsmill-bug-pipeline opsmill-bug-pipeline Bot added the state/needs-human-fix The bug agent pipeline could not fix this issue automatically label Jun 29, 2026
@polmichel polmichel merged commit ea9a658 into stable Jun 29, 2026
63 checks passed
@polmichel polmichel deleted the ai-bug-pipeline-9742-webhook-render branch June 29, 2026 21:09
polmichel added a commit that referenced this pull request Jul 1, 2026
…#9747)

* feat(trigger): add jinja_parameter helper for action parameters

Provide a single helper that marks a single-expression Jinja action parameter
as an explicit jinja kind, so Prefect renders it as a plain string instead of
JSON-serializing the rendered value.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>

* fix(webhook): render action parameters as plain strings (#9742)

Emit the webhook trigger's scalar action parameters via the jinja_parameter
helper so a firing automation renders the event id, occurred time, and an
absent branch resource as strings, dispatching the webhook-process deployment
for every triggering event including events with no associated branch.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>

* refactor: render trigger action parameters as plain strings

Apply the jinja_parameter helper to the remaining trigger builders so their
single-expression action parameters render as plain strings, hardening them
against the same serialization failure when an event lacks a referenced
resource key.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>

* chore: add changelog fragments for #9742

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>

* docs: document trigger action-parameter rendering contract

Add a knowledge-doc section describing how trigger action parameters are
rendered server-side by Prefect and why single-expression values must be
emitted via the jinja_parameter helper, and correct a stale builder
reference in the webhook doc.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>

* test: set an explicit occurred datetime on the webhook render event

Pass the event's occurred time explicitly so the datetime value the comment
describes is visible in the test rather than relying on the model default.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>

* test: drop brittle render test that exercised Prefect internals

The unit test replayed Prefect's private parameter-render path to assert
template output. The emit-contract is covered structurally by the component
test and the end-to-end behavior by the live functional test, so remove the
test that coupled to Prefect internals.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

group/backend Issue related to the backend (API Server, Git Agent) state/needs-human-fix The bug agent pipeline could not fix this issue automatically

Projects

None yet

Development

Successfully merging this pull request may close these issues.

bug: webhook automations crash during Prefect parameter render

2 participants