Skip to content

Normalize BigQuery DTS sensor expected statuses after rendering - #70528

Merged
shahar1 merged 3 commits into
apache:mainfrom
mitre88:fix-bigquery-dts-sensor-template
Sep 22, 2026
Merged

shahar1 merged 3 commits into
apache:mainfrom
mitre88:fix-bigquery-dts-sensor-template

Conversation

@mitre88

@mitre88 mitre88 commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

Part of the template-field validation burn-down tracked in #70296.

BigQueryDataTransferServiceTransferRunSensor lists expected_statuses in template_fields but converts it to TransferState members in __init__ via _normalize_state_list, so passing a Jinja expression raised KeyError at parse time. The sensor now stores the raw value and normalizes it in poke, after rendering.

Added a test constructing the sensor with a templated expected_statuses that passes once the field holds the rendered value — it fails against the previous implementation. The class is removed from the exemption list and the validate-operators-init check passes locally.

Per the discussion in #70505 this is a genuine value transformation (not an argument-provision check), so it belongs at execute time.


Was generative AI tooling used to co-author this PR?
  • Yes — Claude Code (Fable 5)

Generated-by: Claude Code (Fable 5) following the guidelines

@potiuk potiuk added the ready for maintainer review Set by triage when all criteria pass, or by the fork CI reconciler when fork CI is green. label Jul 28, 2026

@potiuk potiuk left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Thanks — correct, and this one is the clearest case of the pattern. expected_statuses is in template_fields, so normalising it in __init__ ran against the raw "{{ ... }}" string: _normalize_state_list would try TransferState["{{ VAR.VALUE.EXPECTED_STATUS }}"] and blow up at parse time before the value ever existed. Deferring to poke is the only way this can work.

A side benefit worth noting: self.expected_statuses now holds the rendered value rather than a set[TransferState], which makes the rendered-template view in the UI show what the user actually wrote instead of an enum set.

One observation inline about what a bad value now looks like.


Drafted-by: Claude Code (Opus 5); reviewed by @potiuk before posting

@github-actions

Copy link
Copy Markdown
Contributor

This pull request has been automatically marked as stale because it has not had recent activity. It will be closed in 5 days if no further activity occurs. Thank you for your contributions.

@github-actions github-actions Bot added the stale Stale PRs per the .github/workflows/stale.yml policy file label Sep 21, 2026
Rebased onto current main. Validate expected_statuses after template
rendering so templated values are checked at runtime.
@shahar1 shahar1 removed the stale Stale PRs per the .github/workflows/stale.yml policy file label Sep 22, 2026
Since a prior change moved expected_statuses normalization from
__init__ into poke() (expected_statuses is a template field, so it
has to be normalized against its rendered value rather than the raw
Jinja string), a misspelled status name no longer fails at Dag parse
time. Instead, the TransferState[state.upper()] lookup raises a bare
KeyError from inside poke().

A bare KeyError carries nothing but the key. BaseSensorOperator in
task-sdk wraps every poke() call in a broad except Exception
(task-sdk/src/airflow/sdk/bases/sensor.py), and that arm is
type-agnostic, so the control flow is the same either way: with
silent_fail=True the error is logged and poke_return becomes False,
with never_fail=True it is chained into an AirflowSkipException, and
otherwise it propagates and fails the task. What changes is what the
user reads in each of those three places.

Catch the KeyError where the string lookup happens and re-raise it as
a ValueError that names the offending value and lists the valid
TransferState names, so the failure is immediately actionable
wherever it surfaces.
The templated expected_statuses test hand-assigned the rendered value
instead of exercising Airflow's template rendering machinery, so it
never actually proved that expected_statuses renders correctly at
poke time. It now asserts the raw Jinja string survives construction
and calls render_template_fields() to render it for real before
poking.

Also mark run_id, transfer_config_id, expected_statuses, and
project_id as (templated) in the class docstring, since they are all
listed in template_fields but only impersonation_chain was marked.
@shahar1
shahar1 force-pushed the fix-bigquery-dts-sensor-template branch from dc4e9b5 to 218c520 Compare September 22, 2026 14:10
@shahar1
shahar1 merged commit 9a1273c into apache:main Sep 22, 2026
86 checks passed
@github-actions

Copy link
Copy Markdown
Contributor

Backport failed to create: v3-3-test. View the failure log Run details

Note: As of Merging PRs targeted for Airflow 3.X
the committer who merges the PR is responsible for backporting the PRs that are bug fixes (generally speaking) to the maintenance branches.

In matter of doubt please ask in #release-management Slack channel.

Status Branch Result
❌ v3-3-test Commit Link

You can attempt to backport this manually by running:

cherry_picker 9a1273c v3-3-test

This should apply the commit to the v3-3-test branch and leave the commit in conflict state marking
the files that need manual conflict resolution.

After you have resolved the conflicts, you can continue the backport process by running:

cherry_picker --continue

If you don't have cherry-picker installed, see the installation guide.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:dev-tools area:providers provider:google Google (including GCP) related issues ready for maintainer review Set by triage when all criteria pass, or by the fork CI reconciler when fork CI is green.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants