From a2b0d0d3c1ba009418ba5efccd909b7eab2b2cab Mon Sep 17 00:00:00 2001 From: Quratulain-bilal Date: Wed, 29 Jul 2026 01:49:45 +0500 Subject: [PATCH] fix: add timeout to prompt step subprocess execution (#3768) The prompt step subprocess.run() had no timeout, allowing a hung LLM invocation to block the entire workflow engine indefinitely. The shell step already defaults to 300s timeout. Add timeout parameter (defaulting to 300s, matching shell step) and handle subprocess.TimeoutExpired gracefully. --- src/specify_cli/workflows/steps/prompt/__init__.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/specify_cli/workflows/steps/prompt/__init__.py b/src/specify_cli/workflows/steps/prompt/__init__.py index ed92e1eea..f8c2de3b3 100644 --- a/src/specify_cli/workflows/steps/prompt/__init__.py +++ b/src/specify_cli/workflows/steps/prompt/__init__.py @@ -89,8 +89,9 @@ class PromptStep(StepBase): ) # Attempt CLI dispatch + timeout = config.get("timeout", 300) dispatch_result = self._try_dispatch( - prompt, integration, model, context + prompt, integration, model, context, timeout=timeout ) output: dict[str, Any] = { @@ -136,6 +137,7 @@ class PromptStep(StepBase): integration_key: str | None, model: str | None, context: StepContext, + timeout: int = 300, ) -> dict[str, Any] | None: """Dispatch *prompt* directly through the integration CLI.""" if not integration_key or not isinstance(integration_key, str) or not prompt: @@ -178,6 +180,7 @@ class PromptStep(StepBase): exec_args, text=True, cwd=str(project_root), + timeout=timeout, ) return { "exit_code": result.returncode, @@ -190,6 +193,12 @@ class PromptStep(StepBase): "stdout": "", "stderr": "Interrupted by user", } + except subprocess.TimeoutExpired: + return { + "exit_code": -1, + "stdout": "", + "stderr": f"Prompt timed out after {timeout} seconds.", + } except OSError: return None