From cf5e86e2abfdd063f22f46ae17068a7b5533d47e Mon Sep 17 00:00:00 2001 From: Lichao Chen Date: Thu, 8 Oct 2026 09:05:44 -0700 Subject: [PATCH] fix: preserve empty dict handler output Only keep 'output': {} when the handler itself returned {}; output emptied by popping 'error'/'refresh_worker' is still dropped, so main's existing results are unchanged ({'error': 'test'} does not gain 'output': {}). Strengthen the refresh_worker test to assert the exact result {'stopPod': true}. --- runpod/serverless/modules/rp_job.py | 3 ++- .../test_modules/test_fastapi.py | 21 +++++++++++++++++++ .../test_serverless/test_modules/test_job.py | 2 +- 3 files changed, 24 insertions(+), 2 deletions(-) diff --git a/runpod/serverless/modules/rp_job.py b/runpod/serverless/modules/rp_job.py index 108efc4a6..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,7 +282,7 @@ async def run_job(handler: Callable, job: Dict[str, Any]) -> Dict[str, Any]: else: run_result = {"output": job_output} - if run_result.get("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. 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")) 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): """