Repository navigation
Fix 500 for non-dict JSON bodies on variable/connection endpoints - #72683
Merged
pierrejeambrun merged 2 commits intoSep 10, 2026
Merged
pierrejeambrun merged 2 commits into
pierrejeambrun merged 2 commits into
Conversation
FrankYang0529
marked this pull request as ready for review
September 8, 2026 10:46
FrankYang0529
requested review from
bugraoz93,
choo121600,
ephraimbuddy,
henry3260,
jason810496,
pierrejeambrun,
rawwar and
shubhamraj-git
as code owners
September 8, 2026 10:46
Andrushika
approved these changes
Sep 8, 2026
Andrushika
left a comment
Contributor
There was a problem hiding this comment.
LGTM, one nitpick left. Thanks!
vincbeck
approved these changes
Sep 9, 2026
Signed-off-by: PoAn Yang <payang@apache.org>
Signed-off-by: PoAn Yang <payang@apache.org>
FrankYang0529
force-pushed
the
airflow-action-logging-non-dict-body
branch
from
September 10, 2026 01:17
915f1ef to
60e0f60
Compare
Contributor
Backport successfully created: v3-3-testNote: As of Merging PRs targeted for Airflow 3.X In matter of doubt please ask in #release-management Slack channel.
|
github-actions Bot
pushed a commit
to aws-mwaa/upstream-to-airflow
that referenced
this pull request
Sep 10, 2026
…ndpoints (apache#72683) * Fix 500 for non-dict JSON bodies on variable/connection endpoints Signed-off-by: PoAn Yang <payang@apache.org> * Simplify the comment Signed-off-by: PoAn Yang <payang@apache.org> --------- (cherry picked from commit b134148) Co-authored-by: PoAn Yang <payang@apache.org> Signed-off-by: PoAn Yang <payang@apache.org>
aws-airflow-bot
pushed a commit
to aws-mwaa/upstream-to-airflow
that referenced
this pull request
Sep 10, 2026
…ndpoints (apache#72683) * Fix 500 for non-dict JSON bodies on variable/connection endpoints Signed-off-by: PoAn Yang <payang@apache.org> * Simplify the comment Signed-off-by: PoAn Yang <payang@apache.org> --------- (cherry picked from commit b134148) Co-authored-by: PoAn Yang <payang@apache.org> Signed-off-by: PoAn Yang <payang@apache.org>
imrichardwu
pushed a commit
to imrichardwu/airflow
that referenced
this pull request
Sep 11, 2026
…ache#72683) * Fix 500 for non-dict JSON bodies on variable/connection endpoints Signed-off-by: PoAn Yang <payang@apache.org> * Simplify the comment Signed-off-by: PoAn Yang <payang@apache.org> --------- Signed-off-by: PoAn Yang <payang@apache.org>
xvega
pushed a commit
to xvega/airflow
that referenced
this pull request
Sep 13, 2026
…ache#72683) * Fix 500 for non-dict JSON bodies on variable/connection endpoints Signed-off-by: PoAn Yang <payang@apache.org> * Simplify the comment Signed-off-by: PoAn Yang <payang@apache.org> --------- Signed-off-by: PoAn Yang <payang@apache.org>
kaxil
added a commit
to astronomer/airflow
that referenced
this pull request
Sep 15, 2026
action_logging reads the request body to record it in the audit log, and parsed it with no error handling. Whether that mattered depended on the route it is attached to: where the route declares a body, FastAPI parses and validates it first, so a bad body is a 4xx and the dependency never runs. Where the route declares no body -- the deletes, the favourite toggles, the backfill pause/cancel actions -- FastAPI never touches the body, so this dependency was the only thing that parsed it, and the error escaped as a 500 for a request whose endpoint does not read a body at all. The action was lost with it: a DELETE that answered 500 left the variable in place. A body this dependency cannot read is now recorded as an access without a body. It runs before the route so an access is logged whether or not the route succeeds, which also means it must never be the reason a request fails. Returning 400 instead would put validation of a field the API does not declare inside the audit logger. The catch is broad, matching what FastAPI does around the same call. json.loads raises JSONDecodeError for malformed input, UnicodeDecodeError for invalid UTF-8, a bare ValueError for an integer over int_max_str_digits, and RecursionError for deeply nested input. Three earlier fixes to this line each guarded a value json.loads returned -- an empty body (apache#49035), a list (apache#62354), then non-dict bodies again (apache#72683) -- and enumerating types is how each one missed the next shape. requires_access_backfill parses the same body to find a dag_id and suppressed only JSONDecodeError, so it re-raised on the others. A body it cannot read names no Dag, which is the state it already handles, so it now suppresses them the same way. This is reachable only when backfill_id is absent or unparsable, since a valid one is authorized from the path without reading the body. The test enumerates the routes from the app and sends every input class that makes json.loads raise, rather than naming the endpoint of the latest report. It requires an audit row per route as well as a non-500, so a route rejected by an earlier dependency cannot pass while proving nothing.
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.
Why
action_loggingis a FastAPI dependency, and dependencies are solved before Pydantic validates the request body. It sees whateverjson.loadsproduced, so a list, string or number reaches it as valid JSON.request_body.items()with no type guard, soPOST /api/v2/variablesandPOST /api/v2/connectionsraiseAttributeErrorand return 500 instead of the 422 the OpenAPI spec declares.action_loggingcommits its audit row before the endpoint runs, so an attempt is recorded even when the request is later rejected. The crash happens before that commit, so onmainthese requests leave no audit row at all.How
request_bodyonly when it is a dict, so the variable keeps its{}initial value for every other shape.Verification
uv run --frozen --project airflow-core pytest airflow-core/tests/unit/api_fastapi/core_api/routes/public/test_connections.py airflow-core/tests/unit/api_fastapi/core_api/routes/public/test_variables.pyBefore this PR, all six requests return
HTTP 500with a bareInternal Server Error.With this PR they return the 422 the OpenAPI spec declares:
Before this PR, the six rejected requests leave no audit rows at all —
action_loggingraises before reaching itssession.commit(), so the record of the attempt is lost.With this PR every attempt is recorded:
Was generative AI tooling used to co-author this PR?
{pr_number}.significant.rst, in airflow-core/newsfragments. You can add this file in a follow-up commit after the PR is created so you know the PR number.