Fix Variables API handling of non-string JSON values - #71018
Open
kaxil wants to merge 1 commit into
Open
Conversation
POST /api/v2/variables with a non-string JSON value (array, object, bool, null) silently stored the Python repr of the value, which cannot be read back with deserialize_json. PATCH with the same payload failed with a masked 500 because the raw value hit the ORM Fernet encryption step. VariableBody now JSON-encodes non-string values once at request validation, covering POST, PATCH and bulk consistently, matching the behaviour the bulk create path already had for dicts and lists. Variable.set_val also raises a clear TypeError instead of the cryptic "encoding without a string argument".
kaxil
marked this pull request as ready for review
August 3, 2026 20:37
kaxil
requested review from
XD-DENG,
ashb,
bugraoz93,
choo121600,
ephraimbuddy,
henry3260,
jason810496,
pierrejeambrun,
rawwar and
shubhamraj-git
as code owners
August 3, 2026 20:37
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #71010
Closes #71015
Problem
VariableBody.valueis typedJsonValue, so the Variables REST API accepts any JSON type, but everything downstream assumed a string:POST /api/v2/variableswith"value": ["a", "b"]returned 201 but stored the Python repr['a', 'b']. That is not valid JSON, so the variable silently breaks the nextVariable.get(key, deserialize_json=True).PATCH /api/v2/variables/{key}with the same payload failed with a masked 500: the raw list reached the Fernet encryption step, which raisedTypeError: encoding without a string argument."True"/"None".Solution
VariableBodynow JSON-encodes non-string values once, at request validation, so POST, PATCH and both bulk actions behave identically: strings are stored verbatim, everything else is stored as JSON (indent=2, byte-identical to the existing bulk-create format) and round-trips throughdeserialize_json=True. The now-redundantserialize_jsonspecial case in bulk create is removed, andVariable.set_valraises a clearTypeErrorpointing atserialize_json=Trueinstead of the cryptic encoding error."value": ["a", "b"]['a', 'b'], unreadable as JSON["a", "b"]"value": ["a", "b"]"value": true/null"True"/"None"true/null"value": nullnullString values, including JSON passed as a string, are stored byte-for-byte as before, and the OpenAPI schema is unchanged, so generated clients are unaffected.
Why coerce instead of rejecting with 422
Non-string values are an intentional part of the API contract since #49844: the UI Import Variables flow sends raw parsed JSON (ImportVariablesForm.tsx#L71), the docs describe bulk-uploading variables as a JSON file, and
airflow variables importapplies the identical encode-iff-not-a-string rule (variable_command.py#L165). Tighteningvalueback tostrwould 422 all of those.Notes
NaN. Rejecting them in the validator is not viable: the 422 response echoes the NaN input, which starlette'sJSONResponsecannot serialize, turning the rejection into a 500.JWTRefreshMiddlewaremasking the real traceback asRuntimeError: No response returned) is not addressed here; it also affects When trying to edit and save Airflow variables using the Web UI, the save operation fails with an HTTP 500 Internal Server Error. #68868 and API write endpoints return opaque 500 when the database rejects a payload #66889.