conductor-oss/conductor · error · ConflictException

Workflow: %s, version: %s already exists!

Error message

Workflow: %s, version: %s already exists!

What it means

Thrown by CassandraMetadataDAO.createWorkflowDef when a Cassandra lightweight transaction (an INSERT ... IF NOT EXISTS) returns wasApplied()==false, meaning a WorkflowDef row with the same name+version already exists in the workflow_def table. ConflictException maps to an HTTP 409-style duplicate-registration error: the write was rejected because the resource is already present.

Source

Thrown at cassandra-persistence/src/main/java/com/netflix/conductor/cassandra/dao/CassandraMetadataDAO.java:173

        } catch (DriverException e) {
            Monitors.error(CLASS_NAME, "removeTaskDef");
            String errorMsg = String.format("Failed to remove task definition: %s", name);
            LOGGER.error(errorMsg, e);
            throw new TransientException(errorMsg, e);
        }
    }

    @Override
    public void createWorkflowDef(WorkflowDef workflowDef) {
        try {
            String workflowDefinition = toJson(workflowDef);
            if (!session.execute(
                            insertWorkflowDefStatement.bind(
                                    workflowDef.getName(),
                                    workflowDef.getVersion(),
                                    workflowDefinition))
                    .wasApplied()) {
                throw new ConflictException(
                        "Workflow: %s, version: %s already exists!",
                        workflowDef.getName(), workflowDef.getVersion());
            }
            String workflowDefIndex =
                    getWorkflowDefIndexValue(workflowDef.getName(), workflowDef.getVersion());
            session.execute(
                    insertWorkflowDefVersionIndexStatement.bind(
                            workflowDefIndex, workflowDefIndex));
            recordCassandraDaoRequests("createWorkflowDef");
            recordCassandraDaoPayloadSize(
                    "createWorkflowDef", workflowDefinition.length(), "n/a", workflowDef.getName());
        } catch (DriverException e) {
            Monitors.error(CLASS_NAME, "createWorkflowDef");
            String errorMsg =
                    String.format(
                            "Error creating workflow definition: %s/%d",
                            workflowDef.getName(), workflowDef.getVersion());
            LOGGER.error(errorMsg, e);

View on GitHub (pinned to cf7c3e4a8a)

Solutions

  1. Call updateWorkflowDef (or an upsert path) instead of create when the version may already exist.
  2. Bump the WorkflowDef version to register the definition as a new variant.
  3. Remove the existing definition (removeWorkflowDef) before re-creating it.
  4. Make registration scripts idempotent: check getWorkflowDef(name, version) first and skip or update when present.

Example fix

// before: always create, fails on re-run
metadataDAO.createWorkflowDef(def);

// after: idempotent check-then-create/update
if (metadataDAO.getWorkflowDef(def.getName(), def.getVersion()).isPresent()) {
    metadataDAO.updateWorkflowDef(def);
} else {
    metadataDAO.createWorkflowDef(def);
}
Defensive patterns

Strategy: validation

Validate before calling

// Check existence before creating to avoid the conflict
boolean exists = metadataDAO
        .getWorkflowDef(def.getName(), def.getVersion())
        .isPresent();
if (exists) {
    throw new IllegalStateException(
        "Workflow " + def.getName() + "/" + def.getVersion()
        + " already exists; use update instead of create");
}

Try / catch

// Treat a conflict as 'already registered' and fall back to update
try {
    metadataDAO.createWorkflowDef(def);
} catch (ConflictException e) {
    LOGGER.info("Workflow already exists, updating: {}/{}",
        def.getName(), def.getVersion());
    metadataDAO.updateWorkflowDef(def);
}

Prevention

When it happens

Trigger: Calling the metadata API (or MetadataDAO.createWorkflowDef) to register a workflow definition whose name+version pair already has a row committed in Cassandra. The LWT compare-and-set detects the existing key and rolls back, so wasApplied() is false.

Common situations: Re-running a deployment/bootstrap script that re-registers the same version; CI pipelines that create (rather than upsert) on every run; importing/migrating definitions without checking existence first; two concurrent registrants racing on the same version.

Related errors


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