{"record":{"id":"35b94c2522e22cef","repo":"elsa-workflows/elsa-core","slug":"23505-unable-to-save-data","errorCode":"23505","errorMessage":"Unable to save data","messagePattern":"Unable to save data","errorType":"exception","errorClass":"UniqueKeyConstraintViolationException","httpStatus":null,"severity":"error","filePath":"src/modules/Elsa.Persistence.EFCore.PostgreSql/Handlers/DbExceptionTransformer.cs","lineNumber":18,"sourceCode":"using Elsa.Workflows.Exceptions;\nusing JetBrains.Annotations;\nusing Npgsql;\n\nnamespace Elsa.Persistence.EFCore.PostgreSql.Handlers;\n\n/// <summary>\n/// Transforms database exceptions encountered when using a postgreSQL database into more generic exceptions.\n/// </summary>\n[UsedImplicitly]\npublic class DbExceptionTransformer : IDbExceptionHandler\n{\n    public Task HandleAsync(DbUpdateExceptionContext context)\n    {\n        var exception = context.Exception;\n        \n        if (exception.InnerException is PostgresException { SqlState: \"23505\" })\n            throw new UniqueKeyConstraintViolationException(\"Unable to save data\", exception);\n\n        throw new DataProcessingException(\"Unable to save data\", exception);\n    }\n}","sourceCodeStart":1,"sourceCodeEnd":22,"githubUrl":"https://github.com/elsa-workflows/elsa-core/blob/fe9217bdfa0e27f0e09e45006eb6898f616e513d/src/modules/Elsa.Persistence.EFCore.PostgreSql/Handlers/DbExceptionTransformer.cs#L1-L22","documentation":"The PostgreSQL DbExceptionTransformer handler converts EF Core DbUpdateException whose inner PostgresException has SqlState 23505 (unique_violation) into Elsa's UniqueKeyConstraintViolationException with message 'Unable to save data'. This normalizes provider-specific constraint errors into a typed exception the application/persistence layer can handle.","triggerScenarios":"Saving entities via EF Core SaveChanges when a UNIQUE constraint or unique index is violated in PostgreSQL (SqlState 23505) — e.g. inserting a duplicate workflow instance ID, duplicate definition version, or any key uniqueness conflict.","commonSituations":"Race condition where two concurrent workflow executions create the same record; re-importing/re-registering the same workflow definition; seeding data that already exists; unique index added by migrations conflicting with legacy duplicate rows.","solutions":["Catch UniqueKeyConstraintViolationException around save operations and implement idempotent upsert/skip logic.","Check the violated key in the inner exception's Detail/ConstraintName to see which unique index fired.","Deduplicate existing rows in the table if a new migration introduced a unique index over non-unique data.","Use deterministic IDs or optimistic concurrency instead of relying on auto-generated values that can collide."],"exampleFix":"// before\nawait dbContext.SaveChangesAsync();\n// after\ntry { await dbContext.SaveChangesAsync(); }\ncatch (UniqueKeyConstraintViolationException ex)\n{\n    // handle duplicate key, e.g. load existing and update instead of insert\n}","handlingStrategy":"try-catch","validationCode":"// Pre-check for existence before insert where possible\nvar exists = await dbSet.AnyAsync(x => x.Id == entity.Id, ct);\nif (exists) return await UpdateExistingAsync(entity, ct);","typeGuard":null,"tryCatchPattern":"try\n{\n    await dbContext.SaveChangesAsync(ct);\n}\ncatch (UniqueKeyConstraintViolationException ex)\n{\n    logger.LogInformation(ex.InnerException, \"Duplicate key on save; applying idempotent handling\");\n    // upsert or skip\n}","preventionTips":["Use deterministic business keys and check-before-insert for idempotent operations.","Guard concurrent workflow registration with locking or upsert semantics.","Review unique indexes added by migrations against existing data before deploying.","Catch the typed UniqueKeyConstraintViolationException rather than raw DbUpdateException."],"tags":["postgresql","efcore","unique-constraint","persistence"],"backgroundTag":"database-write-failed","analyzedSha":"fe9217bdfa0e27f0e09e45006eb6898f616e513d","analyzedAt":"2026-09-13T20:32:34.702Z","contentChangedAt":"2026-09-13T20:32:34.702Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}