From c49dec9567e2d5ceb3ae2e4f6c8995db146ef3b4 Mon Sep 17 00:00:00 2001 From: Lichao Chen Date: Thu, 9 Jul 2026 10:05:40 -0700 Subject: [PATCH 1/2] fix: preserve empty dict handler output --- runpod/serverless/modules/rp_job.py | 3 --- .../test_modules/test_fastapi.py | 21 +++++++++++++++++++ 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/runpod/serverless/modules/rp_job.py b/runpod/serverless/modules/rp_job.py index a45cebc68..4e9f3750c 100644 --- a/runpod/serverless/modules/rp_job.py +++ b/runpod/serverless/modules/rp_job.py @@ -279,9 +279,6 @@ async def run_job(handler: Callable, job: Dict[str, Any]) -> Dict[str, Any]: else: run_result = {"output": job_output} - if run_result.get("output") == {}: - run_result.pop("output") - check_return_size(run_result) # Checks the size of the return body. except Exception as err: diff --git a/tests/test_serverless/test_modules/test_fastapi.py b/tests/test_serverless/test_modules/test_fastapi.py index 4830b9df0..938d9ab43 100644 --- a/tests/test_serverless/test_modules/test_fastapi.py +++ b/tests/test_serverless/test_modules/test_fastapi.py @@ -198,6 +198,17 @@ def test_runsync(self): "output": {"result": "success"}, } + empty_handler = Mock(return_value={}) + empty_worker_api = rp_fastapi.WorkerAPI({"handler": empty_handler}) + empty_runsync_return = asyncio.run( + empty_worker_api._sim_runsync(default_input_object) + ) + assert empty_runsync_return == { + "id": "test-123", + "status": "COMPLETED", + "output": {}, + } + # Test with generator handler def generator_handler(job): del job @@ -330,6 +341,16 @@ def test_status(self): "output": {"result": "success"}, } + empty_handler = Mock(return_value={}) + empty_worker_api = rp_fastapi.WorkerAPI({"handler": empty_handler}) + asyncio.run(empty_worker_api._sim_run(default_input_object)) + empty_status_return = asyncio.run(empty_worker_api._sim_status("test-123")) + assert empty_status_return == { + "id": "test-123", + "status": "COMPLETED", + "output": {}, + } + # Test webhook caller sent asyncio.run(worker_api._sim_run(input_object_with_webhook)) asyncio.run(worker_api._sim_status("test-123")) From 9d7ca5d1df2753ffc868b1832a9f6159cbe43ae2 Mon Sep 17 00:00:00 2001 From: Lichao Chen Date: Wed, 23 Sep 2026 23:43:38 -0700 Subject: [PATCH 2/2] fix: only preserve handler-returned {} output, not output emptied by key pops Directly removing the empty-output pop breaks test_job_with_errors: popping 'error'/'refresh_worker' can empty the dict, and main expects those results unchanged ({'error': 'test'} must not gain 'output': {}). Track whether the handler itself returned {} and keep 'output' only in that case. Strengthen the refresh_worker test to assert the exact result {'stopPod': true}. --- runpod/serverless/modules/rp_job.py | 4 ++++ tests/test_serverless/test_modules/test_job.py | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/runpod/serverless/modules/rp_job.py b/runpod/serverless/modules/rp_job.py index 8235172bf..8557184c5 100644 --- a/runpod/serverless/modules/rp_job.py +++ b/runpod/serverless/modules/rp_job.py @@ -264,6 +264,7 @@ async def run_job(handler: Callable, job: Dict[str, Any]) -> Dict[str, Any]: ) log.debug(f"Handler output: {job_output}", job["id"]) + empty_handler_output = isinstance(job_output, dict) and not job_output if isinstance(job_output, dict): error_msg = job_output.pop("error", None) @@ -281,6 +282,9 @@ async def run_job(handler: Callable, job: Dict[str, Any]) -> Dict[str, Any]: else: run_result = {"output": job_output} + if run_result.get("output") == {} and not empty_handler_output: + run_result.pop("output") + check_return_size(run_result) # Checks the size of the return body. except Exception as err: # noqa: BLE001 - user handler may raise anything; surface it diff --git a/tests/test_serverless/test_modules/test_job.py b/tests/test_serverless/test_modules/test_job.py index 1ec5ce353..238acc70f 100644 --- a/tests/test_serverless/test_modules/test_job.py +++ b/tests/test_serverless/test_modules/test_job.py @@ -334,7 +334,7 @@ async def test_job_with_refresh_worker(self): job_result = await rp_job.run_job(mock_handler, self.sample_job) - assert job_result["stopPod"] is True + assert job_result == {"stopPod": True} async def test_job_bool_output(self): """