conductor-oss/conductor · critical · TerminateWorkflowException

Error preparing chat completion task input

Error message

Error preparing chat completion task input: %s

What it means

Thrown by ChatCompleteTaskMapper.getMappedTask as a TerminateWorkflowException when any exception occurs while preparing a chat completion task's input (JSON conversion of inputData to ChatCompletion, history collection, or template substitution). It re-throws pre-existing TerminateWorkflowExceptions unchanged but wraps all other exceptions, logging the raw inputData and stack trace first. This terminates the entire workflow, not just the task.

Solutions

  1. Read the two log.error lines emitted before the throw: the first dumps the full inputData, the second the root exception + stack trace.
  2. Validate the chat-complete task input schema (model, messages/userInput, llmProvider) against ChatCompletion.
  3. Ensure every ${...} placeholder in messages has a matching key in promptVariables.
  4. Confirm referenced participant/tool taskRefNames exist earlier in the workflow.
  5. Wrap risky upstream tasks so their output is always mappable to LLMResponse.

Example fix

// before (workflow input missing required fields)
{
  "llmProvider": "openai",
  "model": "gpt-4o"
  // missing messages / userInput -> Jackson/NullPointerException
}

// after
{
  "llmProvider": "openai",
  "model": "gpt-4o",
  "userInput": "Summarize the report",
  "promptVariables": {"topic": "sales"}
}
Defensive patterns

Strategy: validation

Validate before calling

Object input = taskModel.getInputData();
if (input == null || (!(input instanceof Map) && !(input instanceof ChatCompletion))) {
    throw new IllegalArgumentException("chat-complete task input is missing or wrong type");
}
// Ensure at least one prompt source is present
Map<?,?> map = (Map<?,?>) input;
if (!map.containsKey("messages") && !map.containsKey("userInput")) {
    throw new IllegalArgumentException("chat-complete task requires 'messages' or 'userInput'");
}

Try / catch

// TerminateWorkflowException is not catchable in user code (engine-level);
// prevent it by validating input before the mapper runs.
// In a worker, surface mapping failures via task output instead.
try {
    objectMapper.convertValue(inputData, ChatCompletion.class);
} catch (IllegalArgumentException e) {
    // fix the input shape before the task is scheduled

Prevention

When it happens

Trigger: taskModel.getInputData() cannot be converted to ChatCompletion (Jackson mismatch/missing required field), an NPE during getHistory (e.g. a task with null WorkflowTask or missing taskReferenceName), or a StringTemplate.fString failure on a ${...} variable with no replacement value.

Common situations: A workflow author provides chat-complete task input with the wrong shape (missing 'messages'/'userInput'), references a participant/tool taskRefName that does not exist, uses a ${variable} with no promptVariables mapping, or a prior task output that cannot be coerced into LLMResponse.

Related errors


AI-assisted analysis of conductor-oss/conductor@cf7c3e4a8a (2026-08-14). Data as JSON: /api/errors/9a6039b08296ed6b. Report an issue: GitHub.

Appendix: source

Thrown at ai/src/main/java/org/conductoross/conductor/ai/tasks/mapper/ChatCompleteTaskMapper.java:104

                history.add(new ChatMessage(ChatMessage.Role.user, chatCompletion.getUserInput()));
            }
            // getHistory() internally skips prior loop-iteration assistant messages
            // for this same task refName when (a) previousResponseId is in play
            // (OpenAI Responses API server-side store owns the prior turns) or
            // (b) the provider declares it doesn't accept assistant-message
            // prefill (AIModel.supportsAssistantPrefill — e.g. Anthropic, where
            // Claude Sonnet 4.6+ rejects prefill outright). Participants, tool
            // calls, and sub-workflow context are still preserved in both cases.
            getHistory(workflowModel, taskModel, chatCompletion);
            updateTaskModel(chatCompletion, taskModel);

        } catch (Exception e) {
            if (e instanceof TerminateWorkflowException) {
                throw (TerminateWorkflowException) e;
            } else {
                log.error("input: {}", taskModel.getInputData());
                log.error(e.getMessage(), e);
                throw new TerminateWorkflowException(
                        String.format(
                                "Error preparing chat completion task input: %s", e.getMessage()));
            }
        }
        return taskModel;
    }

    protected void updateTaskModel(ChatCompletion chatCompletion, TaskModel simpleTask) {
        Map<String, Object> paramReplacement = chatCompletion.getPromptVariables();
        if (paramReplacement == null) {
            paramReplacement = new HashMap<>();
        }
        List<ChatMessage> messages = chatCompletion.getMessages();
        if (messages == null) {
            messages = new ArrayList<>();
        }
        for (ChatMessage message : messages) {
            String msgText = message.getMessage();

View on GitHub (pinned to cf7c3e4a8a)