From 5c4055c3eae93839e3b19c07a9b0033bc1daf74a Mon Sep 17 00:00:00 2001 From: Arif Dogan Date: Thu, 2 Oct 2025 23:55:18 +0200 Subject: [PATCH] Refactor JSON error handling in routing and tests for improvements. --- fastapi/routing.py | 15 +++--- tests/test_json_error_improvements.py | 48 +++++++++++-------- .../test_body/test_tutorial001.py | 11 +++-- 3 files changed, 40 insertions(+), 34 deletions(-) diff --git a/fastapi/routing.py b/fastapi/routing.py index 34abac126..e670b07bb 100644 --- a/fastapi/routing.py +++ b/fastapi/routing.py @@ -266,10 +266,6 @@ def get_request_handler( else: body = body_bytes except json.JSONDecodeError as e: - lines_before = e.doc[: e.pos].split("\n") - line_number = len(lines_before) - column_number = len(lines_before[-1]) + 1 if lines_before else 1 - start_pos = max(0, e.pos - 40) end_pos = min(len(e.doc), e.pos + 40) error_snippet = e.doc[start_pos:end_pos] @@ -282,14 +278,15 @@ def get_request_handler( [ { "type": "json_invalid", - "loc": ("body", line_number, column_number), - "msg": f"JSON decode error - {e.msg} at line {line_number}, column {column_number}", - "input": error_snippet, + "loc": ("body", e.pos), + "msg": "JSON decode error", + "input": {}, "ctx": { "error": e.msg, "position": e.pos, - "line": line_number, - "column": column_number, + "line": e.lineno - 1, + "column": e.colno - 1, + "snippet": error_snippet, }, } ], diff --git a/tests/test_json_error_improvements.py b/tests/test_json_error_improvements.py index 530062770..764ee13bf 100644 --- a/tests/test_json_error_improvements.py +++ b/tests/test_json_error_improvements.py @@ -30,12 +30,14 @@ def test_json_decode_error_single_line(): assert response.status_code == 422 error = response.json()["detail"][0] - assert error["loc"] == ["body", 1, 27] - assert "line 1" in error["msg"] - assert "column 27" in error["msg"] - assert error["ctx"]["line"] == 1 - assert error["ctx"]["column"] == 27 - assert "None" in error["input"] + assert error["loc"] == ["body", 26] + assert error["msg"] == "JSON decode error" + assert error["input"] == {} + assert error["ctx"]["error"] == "Expecting value" + assert error["ctx"]["position"] == 26 + assert error["ctx"]["line"] == 0 + assert error["ctx"]["column"] == 26 + assert "None" in error["ctx"]["snippet"] def test_json_decode_error_multiline(): @@ -52,12 +54,13 @@ def test_json_decode_error_multiline(): assert response.status_code == 422 error = response.json()["detail"][0] - assert error["loc"] == ["body", 4, 12] - assert "line 4" in error["msg"] - assert "column 12" in error["msg"] - assert error["ctx"]["line"] == 4 - assert error["ctx"]["column"] == 12 - assert "invalid" in error["input"] + assert error["loc"][0] == "body" + assert isinstance(error["loc"][1], int) + assert error["msg"] == "JSON decode error" + assert error["input"] == {} + assert error["ctx"]["line"] == 3 + assert error["ctx"]["column"] == 11 + assert "invalid" in error["ctx"]["snippet"] def test_json_decode_error_shows_snippet(): @@ -70,9 +73,10 @@ def test_json_decode_error_shows_snippet(): assert response.status_code == 422 error = response.json()["detail"][0] - assert "..." in error["input"] - assert "invalid" in error["input"] - assert len(error["input"]) <= 83 + assert error["msg"] == "JSON decode error" + assert error["input"] == {} + assert "invalid" in error["ctx"]["snippet"] + assert len(error["ctx"]["snippet"]) <= 83 def test_json_decode_error_empty_body(): @@ -117,10 +121,13 @@ def test_json_decode_error_unclosed_brace(): assert response.status_code == 422 error = response.json()["detail"][0] - assert "line" in error["msg"].lower() - assert "column" in error["msg"].lower() + assert error["msg"] == "JSON decode error" assert error["type"] == "json_invalid" + assert error["input"] == {} assert "position" in error["ctx"] + assert "line" in error["ctx"] + assert "column" in error["ctx"] + assert "snippet" in error["ctx"] def test_json_decode_error_in_middle_of_long_document(): @@ -135,9 +142,10 @@ def test_json_decode_error_in_middle_of_long_document(): assert response.status_code == 422 error = response.json()["detail"][0] - # The error snippet should have "..." at the end since error is early in doc - assert error["input"].endswith("...") - assert "invalid" in error["input"] + assert error["msg"] == "JSON decode error" + assert error["input"] == {} + assert error["ctx"]["snippet"].endswith("...") + assert "invalid" in error["ctx"]["snippet"] assert error["type"] == "json_invalid" diff --git a/tests/test_tutorial/test_body/test_tutorial001.py b/tests/test_tutorial/test_body/test_tutorial001.py index be22e1301..8aaade066 100644 --- a/tests/test_tutorial/test_body/test_tutorial001.py +++ b/tests/test_tutorial/test_body/test_tutorial001.py @@ -206,14 +206,15 @@ def test_post_broken_body(client: TestClient): "detail": [ { "type": "json_invalid", - "loc": ["body", 1, 2], - "msg": "JSON decode error - Expecting property name enclosed in double quotes at line 1, column 2", - "input": "{some broken json}", + "loc": ["body", 1], + "msg": "JSON decode error", + "input": {}, "ctx": { "error": "Expecting property name enclosed in double quotes", "position": 1, - "line": 1, - "column": 2, + "line": 0, + "column": 1, + "snippet": "{some broken json}", }, } ]