{"record":{"id":"d53d8c14a3f7c46f","repo":"conductor-oss/conductor","slug":"tasks-of-multiple-workflows-cannot-be-created-upda","errorCode":null,"errorMessage":"Tasks of multiple workflows cannot be created/updated simultaneously","messagePattern":"Tasks of multiple workflows cannot be created/updated simultaneously","errorType":"exception","errorClass":"NonTransientException","httpStatus":500,"severity":"error","filePath":"cassandra-persistence/src/main/java/com/netflix/conductor/cassandra/dao/CassandraExecutionDAO.java","lineNumber":844,"sourceCode":"        Preconditions.checkNotNull(tasks, \"Tasks object cannot be null\");\n        Preconditions.checkArgument(!tasks.isEmpty(), \"Tasks object cannot be empty\");\n        tasks.forEach(\n                task -> {\n                    Preconditions.checkNotNull(task, \"task object cannot be null\");\n                    Preconditions.checkNotNull(task.getTaskId(), \"Task id cannot be null\");\n                    Preconditions.checkNotNull(\n                            task.getWorkflowInstanceId(), \"Workflow instance id cannot be null\");\n                    Preconditions.checkNotNull(\n                            task.getReferenceTaskName(), \"Task reference name cannot be null\");\n                });\n\n        String workflowId = tasks.get(0).getWorkflowInstanceId();\n        Optional<TaskModel> optionalTask =\n                tasks.stream()\n                        .filter(task -> !workflowId.equals(task.getWorkflowInstanceId()))\n                        .findAny();\n        if (optionalTask.isPresent()) {\n            throw new NonTransientException(\n                    \"Tasks of multiple workflows cannot be created/updated simultaneously\");\n        }\n    }\n\n    @VisibleForTesting\n    WorkflowMetadata getWorkflowMetadata(String workflowId) {\n        ResultSet resultSet =\n                session.execute(selectTotalStatement.bind(UUID.fromString(workflowId)));\n        recordCassandraDaoRequests(\"getWorkflowMetadata\");\n        return Optional.ofNullable(resultSet.one())\n                .map(\n                        row -> {\n                            WorkflowMetadata workflowMetadata = new WorkflowMetadata();\n                            workflowMetadata.setTotalTasks(row.getInt(TOTAL_TASKS_KEY));\n                            workflowMetadata.setTotalPartitions(row.getInt(TOTAL_PARTITIONS_KEY));\n                            return workflowMetadata;\n                        })\n                .orElseThrow(","sourceCodeStart":826,"sourceCodeEnd":862,"githubUrl":"https://github.com/conductor-oss/conductor/blob/cf7c3e4a8adfb158be778ab1ec525323c363cd3a/cassandra-persistence/src/main/java/com/netflix/conductor/cassandra/dao/CassandraExecutionDAO.java#L826-L862","documentation":"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.","triggerScenarios":"Calling createTasks/updateTasks with a List<TaskModel> whose members have different workflowInstanceId values — e.g. concatenating tasks from two workflows before persisting.","commonSituations":"A batch-accumulation bug where a shared list collects tasks across workflows; refactoring that broke per-workflow grouping; incorrect test fixtures.","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."],"exampleFix":"// before\ntasks.forEach(t -> allTasks.add(t)); // mixes workflows\nexecutionDAO.createTasks(allTasks); // throws\n\n// after\ntasks.stream()\n    .collect(Collectors.groupingBy(TaskModel::getWorkflowInstanceId))\n    .values()\n    .forEach(executionDAO::createTasks);","handlingStrategy":"validation","validationCode":"// Group tasks by workflow before persisting; never mix workflows in one batch\nMap<String, List<TaskModel>> byWorkflow = tasks.stream()\n    .collect(Collectors.groupingBy(TaskModel::getWorkflowInstanceId));\nbyWorkflow.values().forEach(executionDAO::createTasks);","typeGuard":"public boolean isSingleWorkflowBatch(List<TaskModel> tasks) {\n    if (tasks == null || tasks.isEmpty()) return false;\n    String wf = tasks.get(0).getWorkflowInstanceId();\n    return tasks.stream().allMatch(t -> wf.equals(t.getWorkflowInstanceId()));\n}","tryCatchPattern":"try {\n    executionDAO.createTasks(tasks);\n} catch (NonTransientException e) {\n    // programmer error: mixed-workflow batch — do NOT retry; fix grouping upstream\n    LOGGER.error(\"Refusing mixed-workflow task batch\", e);\n    throw e;\n}","preventionTips":["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."],"tags":["cassandra","persistence","non-transient","data-integrity","validation","task","dao"],"backgroundTag":null,"analyzedSha":"cf7c3e4a8adfb158be778ab1ec525323c363cd3a","analyzedAt":"2026-08-14T03:33:19.897Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}