conductor-oss/conductor · error · NonTransientException
Tasks of multiple workflows cannot be created/updated simult
Error message
Tasks of multiple workflows cannot be created/updated simultaneously
What it means
validateTasks throws NonTransientException when the supplied task list spans more than one workflowInstanceId. The Cassandra DAO writes tasks for a single workflow per batch, so mixed-workflow batches are a programming error, not a retriable fault. NonTransientException is NOT retried by the framework RetryTemplate.
Source
Thrown at cassandra-persistence/src/main/java/com/netflix/conductor/cassandra/dao/CassandraExecutionDAO.java:844
Preconditions.checkNotNull(tasks, "Tasks object cannot be null");
Preconditions.checkArgument(!tasks.isEmpty(), "Tasks object cannot be empty");
tasks.forEach(
task -> {
Preconditions.checkNotNull(task, "task object cannot be null");
Preconditions.checkNotNull(task.getTaskId(), "Task id cannot be null");
Preconditions.checkNotNull(
task.getWorkflowInstanceId(), "Workflow instance id cannot be null");
Preconditions.checkNotNull(
task.getReferenceTaskName(), "Task reference name cannot be null");
});
String workflowId = tasks.get(0).getWorkflowInstanceId();
Optional<TaskModel> optionalTask =
tasks.stream()
.filter(task -> !workflowId.equals(task.getWorkflowInstanceId()))
.findAny();
if (optionalTask.isPresent()) {
throw new NonTransientException(
"Tasks of multiple workflows cannot be created/updated simultaneously");
}
}
@VisibleForTesting
WorkflowMetadata getWorkflowMetadata(String workflowId) {
ResultSet resultSet =
session.execute(selectTotalStatement.bind(UUID.fromString(workflowId)));
recordCassandraDaoRequests("getWorkflowMetadata");
return Optional.ofNullable(resultSet.one())
.map(
row -> {
WorkflowMetadata workflowMetadata = new WorkflowMetadata();
workflowMetadata.setTotalTasks(row.getInt(TOTAL_TASKS_KEY));
workflowMetadata.setTotalPartitions(row.getInt(TOTAL_PARTITIONS_KEY));
return workflowMetadata;
})
.orElseThrow(View on GitHub (pinned to cf7c3e4a8a)
Solutions
- Group tasks by workflowInstanceId before calling createTasks/updateTasks; write one batch per workflow.
- Add a precondition at the call site that all tasks share the same workflowInstanceId.
- Fix the upstream accumulator so it never mixes workflows into one list.
- Write a unit test asserting single-workflow batches to prevent regression.
Example fix
// before
tasks.forEach(t -> allTasks.add(t)); // mixes workflows
executionDAO.createTasks(allTasks); // throws
// after
tasks.stream()
.collect(Collectors.groupingBy(TaskModel::getWorkflowInstanceId))
.values()
.forEach(executionDAO::createTasks); Defensive patterns
Strategy: validation
Validate before calling
// Group tasks by workflow before persisting; never mix workflows in one batch
Map<String, List<TaskModel>> byWorkflow = tasks.stream()
.collect(Collectors.groupingBy(TaskModel::getWorkflowInstanceId));
byWorkflow.values().forEach(executionDAO::createTasks); Type guard
public boolean isSingleWorkflowBatch(List<TaskModel> tasks) {
if (tasks == null || tasks.isEmpty()) return false;
String wf = tasks.get(0).getWorkflowInstanceId();
return tasks.stream().allMatch(t -> wf.equals(t.getWorkflowInstanceId()));
} Try / catch
try {
executionDAO.createTasks(tasks);
} catch (NonTransientException e) {
// programmer error: mixed-workflow batch — do NOT retry; fix grouping upstream
LOGGER.error("Refusing mixed-workflow task batch", e);
throw e;
} Prevention
- Always partition task lists by workflowInstanceId before create/update.
- Add an assertion at the boundary that the batch is single-workflow.
- Write a unit test that feeds a mixed list and expects the NonTransientException.
When it happens
Trigger: Calling createTasks/updateTasks with a List<TaskModel> whose members have different workflowInstanceId values — e.g. concatenating tasks from two workflows before persisting.
Common situations: A batch-accumulation bug where a shared list collects tasks across workflows; refactoring that broke per-workflow grouping; incorrect test fixtures.
Related errors
- Invalid row with entityKey: %s found in datastore for workfl
- Failed to remove task: %s
- Failed to remove task lookup: %s
- Failed to lookup workflowId from taskId: %s
- message + " " + uuidString
AI-assisted analysis of conductor-oss/conductor@cf7c3e4a8a (2026-08-14).
Data as JSON: /api/errors/d53d8c14a3f7c46f.
Report an issue: GitHub.