{"record":{"id":"2214dbc4eb9ddb36","repo":"conductor-oss/conductor","slug":"agent-conductor-requires-prompt-when-resuming","errorCode":null,"errorMessage":"AGENT (conductor) requires 'prompt' when resuming an execution","messagePattern":"AGENT \\(conductor\\) requires 'prompt' when resuming an execution","errorType":"validation","errorClass":"NonRetryableException","httpStatus":null,"severity":"error","filePath":"ai/src/main/java/org/conductoross/conductor/ai/agent/ConductorAgentDelegate.java","lineNumber":111,"sourceCode":"    /** Best-effort cancellation hook used by embedded parent cancellation. */\n    public void cancel(Task task, String reason) {\n        String executionId =\n                asString(\n                        task.getOutputData() != null\n                                ? task.getOutputData().get(ConductorAgentResults.KEY_EXECUTION_ID)\n                                : null);\n        if (StringUtils.isBlank(executionId)) {\n            executionId = StringUtils.trimToNull(parseRequest(task).getExecutionId());\n        }\n        cancelBestEffort(\n                executionId, StringUtils.defaultIfBlank(reason, \"Cancelled by parent workflow\"));\n    }\n\n    private ConductorAgentExecution startOrResume(Task task, ConductorAgentRequest request) {\n        String executionId = StringUtils.trimToNull(request.getExecutionId());\n        if (executionId != null) {\n            if (StringUtils.isBlank(request.getPrompt())) {\n                throw new NonRetryableException(\n                        \"AGENT (conductor) requires 'prompt' when resuming an execution\");\n            }\n            conductorAgentClient.respond(\n                    ConductorAgentRespondRequest.builder()\n                            .executionId(executionId)\n                            .body(Map.of(\"result\", request.getPrompt()))\n                            .build());\n            return fromStatus(conductorAgentClient.getAgentStatus(executionId), null);\n        }\n\n        if (StringUtils.isBlank(request.getPrompt())) {\n            throw new NonRetryableException(\"AGENT requires 'prompt'\");\n        }\n        request.setIdempotencyKey(\n                StringUtils.firstNonBlank(request.getIdempotencyKey(), idempotencyKey(task)));\n        ConductorAgentStartResponse response = conductorAgentClient.startAgent(request);\n        return ConductorAgentExecution.builder()\n                .executionId(response.getExecutionId())","sourceCodeStart":93,"sourceCodeEnd":129,"githubUrl":"https://github.com/conductor-oss/conductor/blob/cf7c3e4a8adfb158be778ab1ec525323c363cd3a/ai/src/main/java/org/conductoross/conductor/ai/agent/ConductorAgentDelegate.java#L93-L129","documentation":"Thrown by ConductorAgentDelegate.startOrResume when the AGENT task input carries an 'executionId' (meaning the run already exists and should be resumed) but the 'prompt' field is blank. On the resume path the prompt is sent verbatim as the response body (Map.of(\"result\", request.getPrompt())), so a missing prompt leaves nothing to respond with. It is a NonRetryableException, so the owning Conductor task is marked FAILED_WITH_TERMINAL_ERROR and will not be retried.","triggerScenarios":"A workflow supplies a task input with executionId set (to resume an in-flight agent run, e.g. to answer a WAITING/tool-request) but omits or blanks the prompt field. This happens when a parent workflow reads executionId from a prior task's output but forgets to also pass the user/tool reply as prompt.","commonSituations":"Human-in-the-loop or tool-call callbacks where the parent workflow forwards the agent's executionId but not the collected answer; a JSONPath expression that resolves the executionId correctly but yields an empty/null for the prompt; resuming after a WAITING state without populating the reply.","solutions":["Provide a non-blank 'prompt' in the AGENT task input alongside 'executionId' — this value becomes the response sent back to the agent run.","If you only want to poll status (not submit a reply), leave executionId OUT of the input so the task starts/polls a fresh run instead of taking the resume branch.","Check the upstream task that produces the executionId and ensure the reply text is mapped into the same input payload under 'prompt'."],"exampleFix":"// before (resume path, prompt missing)\n{ \"executionId\": \"${resume.output.executionId}\" }\n// after\n{ \"executionId\": \"${resume.output.executionId}\", \"prompt\": \"${user_input.text}\" }","handlingStrategy":"validation","validationCode":"// Before calling the AGENT task, ensure resume inputs are complete\nString executionId = (String) taskInput.get(\"executionId\");\nString prompt = (String) taskInput.get(\"prompt\");\nif (executionId != null && !executionId.isBlank()\n        && (prompt == null || prompt.isBlank())) {\n    throw new IllegalStateException(\n        \"Resuming agent execution \" + executionId + \" requires a non-blank 'prompt'\");\n}","typeGuard":null,"tryCatchPattern":"// NonRetryableException -> task fails terminally; catch only to log/metric, not to retry\ntry {\n    delegate.execute(task);\n} catch (NonRetryableException e) {\n    log.error(\"Agent resume rejected (will not retry): {}\", e.getMessage());\n    throw e;\n}","preventionTips":["When wiring a resume, always bind BOTH executionId and prompt in the same input payload.","If you only want to poll an existing run, omit executionId from the input to avoid the resume branch.","Unit-test the parent workflow's output mapping so a missing reply is caught before the AGENT task runs."],"tags":["conductor","ai-agent","validation","non-retryable","task-input"],"backgroundTag":null,"analyzedSha":"cf7c3e4a8adfb158be778ab1ec525323c363cd3a","analyzedAt":"2026-08-14T03:33:19.897Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}