Apache Airflow 3.2.2: SMTP Validation, Triggerer Watchdog, and Prefix Search

   |   4 minute read   |   Using 782 words

Apache Airflow 3.2.2 was published on May 29 2026 with a mix of security tightening, a triggerer reliability fix, and a UI search behavior change that operators of large deployments should know about.

The full notes and downloads are on the GitHub release page. The release is stable, not a candidate.

SMTP STARTTLS now validates certificates

airflow.utils.email.send_email previously called starttls() with no SSL context, so any certificate was accepted. From 3.2.2 the STARTTLS path validates against the system CA bundle by default, matching the behavior the SMTP_SSL path already had.

Deployments pointing Airflow at an SMTP server with a self signed cert have two options:

  • Fix the cert chain on the SMTP side.
  • Set email.ssl_context = "none" in airflow.cfg to keep the old behavior.

The default value, also used when the option is unset, now means “use ssl.create_default_context”. Change tracked in #65346.

Prefix search in the UI with a “Match anywhere” toggle

The Airflow UI used to call REST endpoints with *_pattern parameters, which translate to ILIKE '%term%' substring scans. On large deployments that means a full table scan on every keystroke.

3.2.2 switches the UI to *_prefix_pattern parameters (LIKE 'term%'), which a B-tree index can serve via a range scan. PR #64963 made the switch. PR #66015 then added a per search bar toggle, a small regex icon next to each value, that flips the input back to substring matching for users who relied on the old behavior.

The REST API itself keeps both forms. Existing clients that hit *_pattern parameters keep working exactly as before.

Triggerer race, deadlock, and a new watchdog

This is the big one for anyone running deferred tasks at scale. Triggers calling synchronous SDK methods (for example get_task_states used by safe_to_cancel in several Google provider operators) could crash the triggerer’s internal subprocess. The triggerer kept heartbeating normally and looked healthy to the scheduler, but processed zero triggers. Every deferred task then timed out.

A partial fix in 3.2.1 (#64882) introduced a different deadlock with the same symptom under load. 3.2.2 replaces the lock based serialization with response multiplexing. Each request carries a unique ID, and the response is routed back to the right caller, so concurrent calls from trigger threads no longer contend or deadlock no matter how many triggers run.

On top of that fix, 3.2.2 ships a triggerer subprocess watchdog. The new config option lives in [triggerer]:

[triggerer]
runner_health_check_threshold = 30

If the subprocess goes silent for longer than the threshold (30 seconds by default), the parent stops updating the heartbeat. The scheduler then detects the hang and reassigns the triggers instead of waiting for each one to time out. Set the value to 0 to disable the watchdog. Tracked in #66412.

Stricter deserialization regex and plugin registered deadline references

Two smaller but breaking changes for anyone who customized the runtime:

  • [core] allowed_deserialization_classes_regexp now uses re.fullmatch() instead of re.match(). A pattern like airflow\.models\.Variable no longer admits airflow.models.Variable_Malicious. Operators that used prefix patterns such as airflow\.models\. to mean “anything under that module” must append .* to keep the old semantics: airflow\.models\..*. The default value is empty, so stock installs are not affected. (#66499)
  • Custom DeadlineReference subclasses must now be registered via a new deadline_references attribute on AirflowPlugin, matching how custom timetables and partition mappers already work. Unregistered references raise DeadlineReferenceNotRegistered at deserialization. (#66737)

Notable bug fixes

The release rolls in a long list of fixes. The ones worth flagging:

  • Deadlock in ti_update_state caused by FOR UPDATE locking on dag_run is gone (#67246).
  • Scheduler stops trusting stale executor success after a defer reschedule (#66431).
  • macOS SIGSEGV in task execution is fixed by switching to fork plus exec (#64874).
  • XCom PATCH and POST store native values instead of json.dumps output (#64220).
  • UI rewrites modulepreload hrefs to the api server static path and loads Monaco workers via a same origin Blob shim (#67548, #67352).
  • Bulk pool delete loses its N+1 query pattern (#66222) and datetime range filters in API queries get optimized (#66696).
  • Migration 0080 is fixed to migrate existing deadline rows on both upgrade and downgrade (#66016).
  • Logout revokes the JWT regardless of the auth manager logout URL (#67289).

Upgrade notes

If you move from 3.2.0 or 3.2.1, expect:

  • SMTP STARTTLS now validates certs. Check the SMTP setup or set email.ssl_context = "none".
  • UI search defaults to prefix matching. End users can flip the “Match anywhere” toggle back to substring per field.
  • Tighter regex semantics for allowed_deserialization_classes_regexp.
  • Custom DeadlineReference classes must be registered through an AirflowPlugin.
  • Migration 0080 runs on both upgrade and downgrade paths.

The Docker image is apache/airflow:3.2.2. Pip users can install apache-airflow==3.2.2 against the matching constraints branch.

Where to get it



denis256 at denis256.dev