Skip to content

Warn when ignore_unknown_values is a no-op in GCSToBigQueryOperator - #71675

Open
jonathangosling wants to merge 2 commits into
apache:mainfrom
jonathangosling:warn-ignore-unknown-values-noop
Open

Warn when ignore_unknown_values is a no-op in GCSToBigQueryOperator#71675
jonathangosling wants to merge 2 commits into
apache:mainfrom
jonathangosling:warn-ignore-unknown-values-noop

Conversation

@jonathangosling

Copy link
Copy Markdown

ignore_unknown_values=True silently does nothing under the operator's default autodetect=True. The load job's schema is inferred from the same source data the values are checked against, so no source field can be unknown to it. Users set the flag expecting fields absent from the destination table to be dropped, and instead get a schema mismatch with nothing in the logs indicating the flag was ignored.

This warns when the assembled load configuration has autodetect truthy, ignoreUnknownValues set, and no schema, pointing at autodetect=None - the setting that makes BigQuery use the destination table's own schema.

The check reads the assembled configuration["load"] rather than the operator attributes, because src_fmt_configs and extra_config can override any of those three keys after they are set (all three are in valid_configs for the relevant source formats). Two of the added test cases cover exactly that.

It also fixes the autodetect docstring, which actively pointed the wrong way: it said the parameter "must be set to True if 'schema_fields' and 'schema_object' are undefined" (None is also valid, and is the documented way to load into an existing table), and that it is "suggested to set to True if table are create outside of Airflow" (for a table created outside Airflow, True is precisely what triggers the mismatch - None is what works). All three states are now documented, and autodetect gets a bool | None annotation matching the existing is False check at the schema-object guard.

Deliberately out of scope: changing the default, making autodetect=False fall back to the table schema, and redesigning the flag. The three-state parameter is an artifact of "autodetect": self.autodetect being serialized unconditionally, so None is the only way to express "omit the field" - worth revisiting separately, but not in a bugfix PR.


Was generative AI tooling used to co-author this PR?
  • Yes (please specify the tool below)

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


  • Read the Pull Request Guidelines for more information. Note: commit author/co-author name and email in commits become permanently public when merged.
  • For fundamental code changes, an Airflow Improvement Proposal (AIP) is needed.
  • When adding dependency, check compliance with the ASF 3rd Party License Policy.
  • For significant user-facing changes create newsfragment: {pr_number}.significant.rst, in airflow-core/newsfragments. You can add this file in a follow-up commit after the PR is created so you know the PR number.

Setting ignore_unknown_values=True has no effect under the default
autodetect=True: the load job's schema is inferred from the same source
data that the values are checked against, so no source field can be
unknown to it. Users reach for the flag expecting fields absent from the
destination table to be dropped, and instead hit a schema mismatch with
no indication that the flag was ignored.

The autodetect docstring made this harder to diagnose. It stated that the
parameter must be True when no schema is supplied, and recommended True
for tables created outside Airflow. Both steer users away from
autodetect=None, which is the setting that makes ignore_unknown_values
behave as expected against an existing table's schema.
@boring-cyborg boring-cyborg Bot added area:providers provider:google Google (including GCP) related issues labels Aug 16, 2026
@boring-cyborg

boring-cyborg Bot commented Aug 16, 2026

Copy link
Copy Markdown

Congratulations on your first Pull Request and welcome to the Apache Airflow community! If you have any issues or are unsure about any anything please check our Contributors' Guide
Here are some useful points:

  • Pay attention to the quality of your code (ruff, mypy and type annotations). Our prek-hooks will help you with that.
  • In case of a new feature add useful documentation (in docstrings or in docs/ directory). Adding a new operator? Check this short guide Consider adding an example Dag that shows how users should use it.
  • Consider using Breeze environment for testing locally, it's a heavy docker but it ships with a working Airflow and a lot of integrations.
  • Be patient and persistent. It might take some time to get a review or get the final approval from Committers.
  • Please follow ASF Code of Conduct for all communication including (but not limited to) comments on Pull Requests, Mailing list and Slack.
  • Be sure to read the Airflow Coding style.
  • Always keep your Pull Requests rebased, otherwise your build might fail due to changes not related to your commits.
    Apache Airflow is a community-driven project and together we are making it better 🚀.
    In case of doubts contact the developers at:
    Mailing List: dev@airflow.apache.org
    Slack: https://s.apache.org/airflow-slack

@jonathangosling
jonathangosling marked this pull request as draft August 16, 2026 12:11
@jonathangosling
jonathangosling marked this pull request as ready for review August 16, 2026 12:16

@SameerMesiah97 SameerMesiah97 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.

I have left some comments.

* ``None`` - neither infer a schema nor supply one, so the destination table's own
schema is used. The table must already exist. This is the setting to use when you want
``ignore_unknown_values`` to drop fields that are present in the source data but absent
from the destination table.

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 is too long. There is no need to go deep into the interaction between this and the other parameters here. I would suggest the below instead:

:param autodetect: [Optional] Whether to infer the schema from the source data for CSV and JSON sources. If ``True``, the schema is inferred from the source data. If ``False``, either ``schema_fields`` or ``schema_object`` must be provided. If ``None``, no schema is supplied and the existing destination table's schema is used. (Default: ``True``).

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Good point, I've updated the docstring with your suggestion.

"itself, so no source field can be unknown to it. To drop source fields that are "
"absent from an existing destination table, set `autodetect=None` so that the "
"destination table's own schema is used instead."
)

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.

  1. This helper is not needed as it is only called once. I would inline it.
  2. The warning is too verbose. I would suggest the below:
self.log.warning(
    "`ignore_unknown_values` has no effect when `autodetect=True` and no schema is provided. "
    "Set `autodetect=None` to use the existing destination table's schema instead."
)

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Updated:

  • Inlined the check and warning.
  • Made the message less verbose. I've used your suggestion, which covers the problem and the suggested fix more concisely.

id="ignore_unknown_values_set_by_extra_config",
),
],
)

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.

Since autodetect is explicitly a three-state parameter, could we also cover autodetect=False here?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

I've added a test case for autodetect=False, supplying the schema_fields as required.


assert mock_warning.call_args_list.count(call(IGNORE_UNKNOWN_VALUES_WARNING)) == (
1 if expects_warning else 0
)

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.

Do these parametrized cases need to assert the complete warning text? The behaviour under test seems to be whether the warning is emitted. Using assert_called_once() / assert_not_called() would make these cases less coupled to the wording, with the message itself asserted in a single test if we want to protect it.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Yes, good suggestion. I've updated to use assert_called_once() / assert_not_called(). On protecting the message, rather than pinning the full text, one test (test_ignore_unknown_values_no_op_warning_names_the_fix) now asserts only that the warning names autodetect=None, since that's the actionable part.
One thing worth flagging - these assertions don't discriminate which warning fired, so they'll break if an unrelated warning is added. Filtering on/asserting against a stable part of the message (i.e. ignore_unknown_values or autodetect=None), rather than the full message, could avoid that. Let me know if you think it's worth it.

Shorten the autodetect docstring to match the density of the surrounding
parameters, and inline the single-use warning helper. Decouple the tests
from the warning wording: the parametrized cases now assert only whether
the warning fires, with a single test checking that it names the fix.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:providers provider:google Google (including GCP) related issues

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants