This is an automated email from the ASF dual-hosted git repository.
pierrejeambrun pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/airflow.git
The following commit(s) were added to refs/heads/main by this push:
new b13414865e1 Fix 500 for non-dict JSON bodies on variable/connection
endpoints (#72683)
b13414865e1 is described below
commit b13414865e100cb181634505f8330d9b5f3baeb2
Author: PoAn Yang <[email protected]>
AuthorDate: Thu Sep 10 19:27:53 2026 +0900
Fix 500 for non-dict JSON bodies on variable/connection endpoints (#72683)
* Fix 500 for non-dict JSON bodies on variable/connection endpoints
Signed-off-by: PoAn Yang <[email protected]>
* Simplify the comment
Signed-off-by: PoAn Yang <[email protected]>
---------
Signed-off-by: PoAn Yang <[email protected]>
---
.../src/airflow/api_fastapi/logging/decorators.py | 6 ++++--
.../core_api/routes/public/test_connections.py | 22 ++++++++++++++++++++++
.../core_api/routes/public/test_variables.py | 18 ++++++++++++++++++
3 files changed, 44 insertions(+), 2 deletions(-)
diff --git a/airflow-core/src/airflow/api_fastapi/logging/decorators.py
b/airflow-core/src/airflow/api_fastapi/logging/decorators.py
index 3ed9dd86c79..b459d784b13 100644
--- a/airflow-core/src/airflow/api_fastapi/logging/decorators.py
+++ b/airflow-core/src/airflow/api_fastapi/logging/decorators.py
@@ -203,8 +203,10 @@ def action_logging(event: str | None = None):
masked_body_json = {}
if has_json_body:
- request_body = await request.json()
- if isinstance(request_body, dict):
+ # Non-dict bodies fall through to the endpoint's own 422.
+ parsed_body = await request.json()
+ if isinstance(parsed_body, dict):
+ request_body = parsed_body
masked_body_json = {k: secrets_masker.redact(v, k) for k, v in
request_body.items()}
if event_name in skip_dry_run_events and
request_body.get("dry_run", True):
diff --git
a/airflow-core/tests/unit/api_fastapi/core_api/routes/public/test_connections.py
b/airflow-core/tests/unit/api_fastapi/core_api/routes/public/test_connections.py
index a9822e5afaf..84563368126 100644
---
a/airflow-core/tests/unit/api_fastapi/core_api/routes/public/test_connections.py
+++
b/airflow-core/tests/unit/api_fastapi/core_api/routes/public/test_connections.py
@@ -368,6 +368,28 @@ class TestPostConnection(TestConnectionEndpoint):
]
}
+ @pytest.mark.parametrize(
+ "body",
+ [
+ [{"connection_id": TEST_CONN_ID, "conn_type": TEST_CONN_TYPE}],
+ '{"connection_id": "a"}',
+ 42,
+ ],
+ ids=["list", "string", "number"],
+ )
+ def test_post_should_respond_422_for_non_dict_json_body(self, test_client,
session, body):
+ """The audit-log dependency reads the body before validation, so a
non-object body still gets a 422."""
+ response = test_client.post("/connections", json=body)
+ assert response.status_code == 422
+ assert response.json()["detail"][0]["loc"] == ["body"]
+ _check_last_log(
+ session,
+ dag_id=None,
+ event="post_connection",
+ logical_date=None,
+ expected_extra={"method": "POST"},
+ )
+
@conf_vars({("core", "multi_team"): "False"})
def test_post_rejects_team_name_when_multi_team_disabled(self,
test_client):
response = test_client.post(
diff --git
a/airflow-core/tests/unit/api_fastapi/core_api/routes/public/test_variables.py
b/airflow-core/tests/unit/api_fastapi/core_api/routes/public/test_variables.py
index ec5f915dc43..9a61130e3c1 100644
---
a/airflow-core/tests/unit/api_fastapi/core_api/routes/public/test_variables.py
+++
b/airflow-core/tests/unit/api_fastapi/core_api/routes/public/test_variables.py
@@ -805,6 +805,24 @@ class TestPostVariable(TestVariableEndpoint):
]
}
+ @pytest.mark.parametrize(
+ "body",
+ [[{"key": "new variable key", "value": "new variable value"}],
'{"key": "a"}', 42],
+ ids=["list", "string", "number"],
+ )
+ def test_post_should_respond_422_for_non_dict_json_body(self, test_client,
session, body):
+ """The audit-log dependency reads the body before validation, so a
non-object body still gets a 422."""
+ response = test_client.post("/variables", json=body)
+ assert response.status_code == 422
+ assert response.json()["detail"][0]["loc"] == ["body"]
+ check_last_log(
+ session,
+ dag_id=None,
+ event="post_variable",
+ logical_date=None,
+ expected_extra={"method": "POST"},
+ )
+
@conf_vars({("core", "multi_team"): "False"})
def test_post_rejects_team_name_when_multi_team_disabled(self,
test_client):
body = {