Background
Template fields are rendered after an operator's constructor runs. Any logic applied to a template-field parameter inside __init__ — validation, type checks, transformation, string interpolation — therefore operates on the un-rendered Jinja expression, not the real value. This is documented in Creating a custom Operator and contributing-docs/05_pull_requests.rst, and it causes real bugs: #69813 (SSHRemoteJobOperator validated a templated remote_base_dir in __init__, so cleanup validation failed for any custom base) is a recent example.
The validate-operators-init prek hook previously only verified that template fields are assigned verbatim at the top level of __init__; it could not see validation calls, conditionals, or transformations (and it did not cover sensors, AwsBaseOperator[...] subclasses, or aws_template_fields(...)-based classes at all). The hook has been extended to detect any non-sanctioned read of a template field in __init__ (PR to follow).
The exemption ratchet
Existing violations are listed in scripts/ci/prek/validate_operators_init_exemptions.txt as path::ClassName entries so the hook can enforce the rule on new code immediately.
- A PR that fixes a class must remove its entry in the same PR — the hook fails on stale exemptions, so this cannot be forgotten.
- New violations cannot be added: only files listed in the exemption file are suppressed.
- This issue is done when the exemption file is empty.
How to fix a class: move the validation / transformation from __init__ into execute() (or the first method that runs after rendering), keep __init__ down to plain self.field = field assignments (defaulting via field or default is fine). Don't forget the corresponding tests.
Please do not open sub-issues for individual entries — comment here or just open a PR referencing this issue.
Current exemption list (snapshot)
The authoritative list is the exemptions file; this checklist is the snapshot at the time the check was introduced. 83 classes across 22 providers.
amazon
anthropic
apache/hive
apache/kafka
apache/spark
cncf/kubernetes
cohere
common/ai
databricks
dbt/cloud
docker
google
microsoft/azure
microsoft/psrp
neo4j
oracle
papermill
snowflake
ssh
standard
teradata
weaviate
Drafted-by: Claude Code (Opus 4.8) (no human review before posting)
Background
Template fields are rendered after an operator's constructor runs. Any logic applied to a template-field parameter inside
__init__— validation, type checks, transformation, string interpolation — therefore operates on the un-rendered Jinja expression, not the real value. This is documented in Creating a custom Operator and contributing-docs/05_pull_requests.rst, and it causes real bugs: #69813 (SSHRemoteJobOperatorvalidated a templatedremote_base_dirin__init__, so cleanup validation failed for any custom base) is a recent example.The
validate-operators-initprek hook previously only verified that template fields are assigned verbatim at the top level of__init__; it could not see validation calls, conditionals, or transformations (and it did not cover sensors,AwsBaseOperator[...]subclasses, oraws_template_fields(...)-based classes at all). The hook has been extended to detect any non-sanctioned read of a template field in__init__(PR to follow).The exemption ratchet
Existing violations are listed in
scripts/ci/prek/validate_operators_init_exemptions.txtaspath::ClassNameentries so the hook can enforce the rule on new code immediately.How to fix a class: move the validation / transformation from
__init__intoexecute()(or the first method that runs after rendering), keep__init__down to plainself.field = fieldassignments (defaulting viafield or defaultis fine). Don't forget the corresponding tests.Please do not open sub-issues for individual entries — comment here or just open a PR referencing this issue.
Current exemption list (snapshot)
The authoritative list is the exemptions file; this checklist is the snapshot at the time the check was introduced. 83 classes across 22 providers.
amazon
AppflowBaseOperator— appflow.py — logic in__init__AwsToAwsBaseOperator— base.py — logic in__init__BedrockCreateKnowledgeBaseOperator— bedrock.py — logic in__init__BedrockRaGOperator— bedrock.py — transformed assignment, logic in__init__, missing assignmentDataSyncOperator— datasync.py — logic in__init__DmsModifyTaskOperator— dms.py — logic in__init__DmsStartReplicationOperator— dms.py — logic in__init__EcsRunTaskOperator— ecs.py — logic in__init__EmrAddStepsOperator— emr.py — logic in__init__GCSToS3Operator— gcs_to_s3.py — logic in__init__GlueDataQualityOperator— glue.py — transformed assignment, logic in__init__, missing assignmentMongoToS3Operator— mongo_to_s3.py — logic in__init__NeptuneStartDbClusterOperator— neptune.py — transformed assignment, missing assignmentNeptuneStopDbClusterOperator— neptune.py — transformed assignment, missing assignmentS3DeleteObjectsOperator— s3.py — logic in__init__S3ToRedshiftOperator— s3_to_redshift.py — logic in__init__SageMakerCreateNotebookOperator— sagemaker.py — logic in__init__SageMakerProcessingOperator— sagemaker.py — logic in__init__StepFunctionStartExecutionOperator— step_function.py — transformed assignment, missing assignmentanthropic
AnthropicAgentSessionOperator— agent.py — logic in__init__apache/hive
HivePartitionSensor— hive_partition.py — logic in__init__NamedHivePartitionSensor— named_hive_partition.py — logic in__init__apache/kafka
ProduceToTopicOperator— produce.py — logic in__init__apache/spark
SparkSubmitOperator— spark_submit.py — logic in__init__cncf/kubernetes
KubernetesInstallKueueOperator— kueue.py — logic in__init__KubernetesPodOperator— pod.py — logic in__init__KubernetesResourceBaseOperator— resource.py — logic in__init__cohere
CohereEmbeddingOperator— embedding.py — logic in__init__common/ai
AgentOperator— agent.py — logic in__init__DocumentLoaderOperator— document_loader.py — logic in__init__databricks
DatabricksCopyIntoOperator— databricks_sql.py — logic in__init__DatabricksReposCreateOperator— databricks_repos.py — logic in__init__DatabricksReposDeleteOperator— databricks_repos.py — logic in__init__DatabricksReposUpdateOperator— databricks_repos.py — logic in__init__DatabricksSQLStatementsSensor— databricks.py — logic in__init__dbt/cloud
DbtCloudGetJobRunArtifactOperator— dbt.py — logic in__init__docker
DockerOperator— docker.py — logic in__init__google
AzureFileShareToGCSOperator— azure_fileshare_to_gcs.py — logic in__init__BigQueryDataTransferServiceTransferRunSensor— bigquery_dts.py — transformed assignment, logic in__init__, missing assignmentBigQueryInsertJobOperator— bigquery.py — logic in__init__BigQueryToMsSqlOperator— bigquery_to_mssql.py — logic in__init__CloudBatchSubmitJobOperator— cloud_batch.py — logic in__init__CloudBuildCreateBuildOperator— cloud_build.py — logic in__init__CloudComposerExternalTaskSensor— cloud_composer.py — logic in__init__CloudDataTransferServiceCreateJobOperator— cloud_storage_transfer_service.py — logic in__init__CloudFunctionDeployFunctionOperator— functions.py — logic in__init__ComputeEngineCopyInstanceTemplateOperator— compute.py — logic in__init__ComputeEngineDeleteInstanceGroupManagerOperator— compute.py — logic in__init__ComputeEngineDeleteInstanceOperator— compute.py — logic in__init__ComputeEngineDeleteInstanceTemplateOperator— compute.py — logic in__init__ComputeEngineInsertInstanceFromTemplateOperator— compute.py — logic in__init__ComputeEngineInsertInstanceGroupManagerOperator— compute.py — logic in__init__ComputeEngineInsertInstanceOperator— compute.py — logic in__init__ComputeEngineInsertInstanceTemplateOperator— compute.py — logic in__init__ComputeEngineInstanceGroupUpdateManagerTemplateOperator— compute.py — logic in__init__ComputeEngineSetMachineTypeOperator— compute.py — logic in__init__DataprocCreateClusterOperator— dataproc.py — logic in__init__DataprocSubmitJobOperator— dataproc.py — logic in__init__GCSDeleteObjectsOperator— gcs.py — logic in__init__GCSFileTransformOperator— gcs.py — logic in__init__GCSListObjectsOperator— gcs.py — logic in__init__GCSToBigQueryOperator— gcs_to_bigquery.py — logic in__init__GCSToGCSOperator— gcs_to_gcs.py — logic in__init__GCSToLocalFilesystemOperator— gcs_to_local.py — logic in__init__GenAIGeminiCreateBatchJobOperator— gen_ai.py — logic in__init__GenAIGeminiCreateEmbeddingsBatchJobOperator— gen_ai.py — logic in__init__GoogleCampaignManagerDeleteReportOperator— campaign_manager.py — logic in__init__microsoft/azure
AzureVirtualMachineStateSensor— compute.py — logic in__init__GCSToAzureBlobStorageOperator— gcs_to_wasb.py — logic in__init__OracleToAzureDataLakeOperator— oracle_to_azure_data_lake.py — logic in__init__microsoft/psrp
PsrpOperator— psrp.py — logic in__init__neo4j
Neo4jOperator— neo4j.py — logic in__init__oracle
OracleToOracleOperator— oracle_to_oracle.py — logic in__init__papermill
PapermillOperator— papermill.py — logic in__init__snowflake
SnowparkContainerJobOperator— snowpark_containers.py — logic in__init__ssh
SSHOperator— ssh.py — logic in__init__SSHRemoteJobOperator— ssh_remote_job.py — logic in__init__standard
BashOperator— bash.py — logic in__init__DateTimeSensor— date_time.py — logic in__init__, missing assignmentHITLOperator— hitl.py — logic in__init__TriggerDagRunOperator— trigger_dagrun.py — logic in__init__teradata
TeradataToTeradataOperator— teradata_to_teradata.py — logic in__init__weaviate
WeaviateIngestOperator— weaviate.py — logic in__init__Drafted-by: Claude Code (Opus 4.8) (no human review before posting)