Background
validate-operators-init flags every read of a template-field parameter in __init__. A companion PR narrows that: a field is None / field is not None check reads whether the argument was passed, not its value, and belongs in the constructor. (With render_template_as_native_obj=True a provided field renders to None, so the same check in execute() reports a supplied argument as missing; and raising at construction surfaces a static authoring mistake as a Dag import error rather than per task instance.)
Replaying both checker versions over every exemption entry removed so far (31 PRs, 48 entries) shows 8 entries across 5 PRs where the old rule forced a change that was not needed. Those PRs are merged and green — nothing is broken — but the files now carry comments and regression tests asserting the placement the docs discourage, and contributors working #70296 learn the rule by reading them.
This issue tracks putting them back. Each is independent; separate PRs are fine.
Revert
Split, don't revert
Leave alone (listed so nobody reverts them by pattern-match)
Not in scope
The other 40 exemption entries removed so far are genuine value reads (ruleset.strip(), isinstance(mongo_query, list), source not in SUPPORTED_SOURCES, ...). They stay flagged under the narrowed rule and must not be reverted.
Verifying a revert
Run the hook on the file after the change — all six classes above are clean under the narrowed rule, so no exemption entries need re-adding:
uv run --script scripts/ci/prek/validate_operators_init.py <path>
related: #70296
Drafted-by: Claude Code (Opus 5) (no human review before posting)
Background
validate-operators-initflags every read of a template-field parameter in__init__. A companion PR narrows that: afield is None/field is not Nonecheck reads whether the argument was passed, not its value, and belongs in the constructor. (Withrender_template_as_native_obj=Truea provided field renders toNone, so the same check inexecute()reports a supplied argument as missing; and raising at construction surfaces a static authoring mistake as a Dag import error rather than per task instance.)Replaying both checker versions over every exemption entry removed so far (31 PRs, 48 entries) shows 8 entries across 5 PRs where the old rule forced a change that was not needed. Those PRs are merged and green — nothing is broken — but the files now carry comments and regression tests asserting the placement the docs discourage, and contributors working #70296 learn the rule by reading them.
This issue tracks putting them back. Each is independent; separate PRs are fine.
Revert
providers/common/ai/.../operators/agent.py(Validate template fields after rendering in common.ai operators #70338) —message_history is not None and enable_hitl_reviewis pure provision. Restore to__init__, reverttest_agent.py::test_message_history_with_hitl_review_raisesto constructor-raises. The comment# message_history is a template field; validate the combination after rendering.states the rule wrongly and must go.providers/databricks/.../operators/databricks_repos.py(Validate Databricks Repos operators' template fields after rendering #70341) — all fourbranch/tag/repo_id/repo_pathchecks inDatabricksReposCreate/Update/DeleteOperatorare pure provision. Restore to__init__; revert the two tests docstringed "rejected at execute, after rendering".providers/neo4j/.../operators/neo4j.py(Fix Neo4jOperator templated query validation #70348) —if cypher is None: raisereturns to__init__. The deprecation-warning placement is a separate question; Show the deprecated Neo4jOperator sql query in Rendered Templates #70373's rationale stands independently.Split, don't revert
providers/common/ai/.../operators/document_loader.py(Validate template fields after rendering in common.ai operators #70338) — thesource_path/source_bytesexclusivity is provision and returns to__init__. The third check,source_bytes is not None and file_type is None, backs the downstreamassert self.file_type is not None;file_typeis a template field, so that assumption only holds after rendering and it stays inexecute(). Update the comment to say which is which — this is the canonical example of the boundary.Leave alone (listed so nobody reverts them by pattern-match)
providers/weaviate/.../operators/weaviate.py(Fix WeaviateIngestOperator input_data validation before rendering #70326) — makinginput_datarequired is already released; reverting would be a second API break. Theexecute()check becomes belt-and-braces and is harmless.providers/teradata/.../transfers/teradata_to_teradata.py(Fix Teradata transfer template field initialization #70392) —self.sql_params or {}at the use site is equivalent. No value in churning it back.Not in scope
The other 40 exemption entries removed so far are genuine value reads (
ruleset.strip(),isinstance(mongo_query, list),source not in SUPPORTED_SOURCES, ...). They stay flagged under the narrowed rule and must not be reverted.Verifying a revert
Run the hook on the file after the change — all six classes above are clean under the narrowed rule, so no exemption entries need re-adding:
related: #70296
Drafted-by: Claude Code (Opus 5) (no human review before posting)