From b35266e9a51765867f591bf1cf13c66d3d8f5489 Mon Sep 17 00:00:00 2001 From: kpdev <156195510+kiannidev@users.noreply.github.com> Date: Mon, 1 Jun 2026 04:08:06 -0700 Subject: [PATCH] Return 4xx when file download storage blob is missing (#15371) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary Fixes [#15369](https://github.com/infiniflow/ragflow/issues/15369) — `GET /api/v1/files/` calls `make_response(None)` when both primary and fallback storage lookups return empty, causing HTTP 500. ## Related Issue Fixes #15369 ## Change Type - [x] Bug fix - [x] Regression tests ## What Changed - **`file_api.download()`** — after fallback `STORAGE_IMPL.get`, return `get_error_data_result(message="This file is empty.")` when `not blob`, matching document REST download semantics. ## Files Changed | File | Change | |------|--------| | `api/apps/restful_apis/file_api.py` | Empty-blob guard before `make_response()` | | `test/testcases/test_web_api/test_file_app/test_file_routes_unit.py` | Regression test | ## Validation ```bash cd /root/gittensor/ragflow pytest test/testcases/test_web_api/test_file_app/test_file_routes_unit.py::test_download_missing_blob_returns_error -v pytest test/testcases/test_web_api/test_file_app/test_file_routes_unit.py::test_download_falls_back_to_document_storage -v ``` ## Test Plan - [x] Both storage paths empty → `"This file is empty."` (no `make_response(None)`) - [x] Existing fallback success test still passes - [ ] CI green --- api/apps/restful_apis/file_api.py | 7 +++++++ .../test_file_app/test_file_routes_unit.py | 16 ++++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/api/apps/restful_apis/file_api.py b/api/apps/restful_apis/file_api.py index b67aa30ff..2815dd681 100644 --- a/api/apps/restful_apis/file_api.py +++ b/api/apps/restful_apis/file_api.py @@ -299,6 +299,13 @@ async def download(tenant_id: str = None, file_id: str = None): if not blob: b, n = File2DocumentService.get_storage_address(file_id=file_id) blob = await thread_pool_exec(settings.STORAGE_IMPL.get, b, n) + if not blob: + logging.warning( + "Download failed: empty blob after primary+fallback lookup (tenant_id=%s, file_id=%s)", + tenant_id, + file_id, + ) + return get_error_data_result(message="This file is empty.") response = await make_response(blob) ext = re.search(r"\.([^.]+)$", file.name.lower()) diff --git a/test/testcases/test_web_api/test_file_app/test_file_routes_unit.py b/test/testcases/test_web_api/test_file_app/test_file_routes_unit.py index c1ff639ac..b95592ad9 100644 --- a/test/testcases/test_web_api/test_file_app/test_file_routes_unit.py +++ b/test/testcases/test_web_api/test_file_app/test_file_routes_unit.py @@ -327,6 +327,22 @@ def test_download_falls_back_to_document_storage(monkeypatch): assert res.headers["ext"] == "txt" +@pytest.mark.p2 +def test_download_missing_blob_returns_error(monkeypatch): + module = _load_file_api_module(monkeypatch) + storage_calls = [] + + def _get(bucket, location): + storage_calls.append((bucket, location)) + return None + + monkeypatch.setattr(module.settings, "STORAGE_IMPL", SimpleNamespace(get=_get)) + res = _run(module.download("tenant1", "file1")) + + assert storage_calls == [("bucket1", "path1"), ("bucket2", "path2")] + assert res["message"] == "This file is empty." + + @pytest.mark.p2 def test_parent_and_ancestors_use_new_routes(monkeypatch): module = _load_file_api_module(monkeypatch)