Skip to content

Make the Variable value non-nullable in the Execution API - #76007

Open
uranusjr wants to merge 2 commits into
apache:mainfrom
astronomer:execution-api-non-null-values
Open

uranusjr wants to merge 2 commits into
apache:mainfrom
astronomer:execution-api-non-null-values

Conversation

@uranusjr

@uranusjr uranusjr commented Oct 30, 2026 •

Copy link
Copy Markdown
Member

Identified while reviewing #75920.

A successful Variable read never carries a null value. Variable.get raises KeyError when nothing is found, and the route turns that into a 404. The Execution API and supervisor schema still typed the value as str | None, so every generated client got a nullable type. This changes the variable value's type to non-nullable, so null only means "not found". Clients pinned to an older Execution API or supervisor schema version can still send null, which keeps storing "None" as before.

The Task SDK allowed Variable.set(key, None), but the None was stringified into "None" before being sent through the wire, so reading the Variable back returns the string "None", not None. This now emits a deprecation warning; behavior is unchanged. Core airflow.models.Variable.set(key, None) called outside a task is not changed.

The Go and TypeScript SDKs follow the in-repo supervisor schema, so they move to 2027-02-28 and get a non-nullable Variable value type. A worker only accepts bundles declaring a schema version it knows, so both SDKs now require Airflow 3.5.0. Their min_airflow_version and compatibility matrices are updated to match. The Java SDK pins a published schema version and is unchanged; it will move in a follow-up once 2027-02-28 is published.

A successful Variable read never carries a null value: a missing
Variable is a 404. The Execution API and supervisor schema still typed
the value as nullable, so generated clients got a nullable type.

Type the value as str in VariableResponse/VariablePostBody and in the
supervisor schema's VariableResult/PutVariable. Version 2027-02-28 of
both carries the change. An older client or runtime that sets a
Variable to null keeps storing the string "None", as before.

Setting a Variable to None from the Task SDK is now deprecated. It
still stores "None", but emits RemovedInAirflow4Warning.

The task and asset state store read endpoints now return 404 for a
stored JSON null, so a successful read never carries null. Writers
already reject null.
Comment on lines 587 to 602
def test_head_version_keeps_arg_bindings(self, real_migrator, startup_details):
from airflow.sdk.api.datamodels._generated import LiteralArgBinding, XComArgBinding

out = real_migrator.downgrade(startup_details, "2026-10-30")
assert out.ti_context.arg_bindings is not None
literal, xcom, defaulted = (a.root for a in out.ti_context.arg_bindings)
assert isinstance(literal, LiteralArgBinding)
assert literal.kind == "literal"
assert literal.value == "uk"
assert literal.name == "country"
assert literal.from_default is False
assert literal.value_schema is None
assert isinstance(xcom, XComArgBinding)
assert xcom.kind == "xcom"
assert xcom.task_id == "extract"
assert xcom.name == "extracted"
assert xcom.value_schema.root == {"type": "object"}
assert isinstance(defaulted, LiteralArgBinding)
assert defaulted.kind == "literal"
assert defaulted.from_default is True
assert defaulted.value_schema.root == {"type": "integer", "format": "int64"}

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

The result is re-validated against Cadwyn’s generated copies of the models for 2026-10-30 in this test, but since now there are actual migrations happening, the classes are those copies, not the head ones, so isinstance fails.

Checking the kind discriminator asserts the same thing, which binding variant this is, without depending on class identity. I kept the target at 2026-10-30 because that’s the version the Java SDK pins, so it’s the downgrade that matters in practice.

This branch has not been deployed

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant