How to fix Liquibase: Change set already exists in PostgreSQL

PostgreSQLINTERMEDIATEHIGHUpdated Dec 28, 2025Β· 144 views

This error occurs when Liquibase detects a changeset identifier that has already been deployed to your PostgreSQL database. It happens when you try to re-run a migration that Liquibase has marked as executed, or when duplicate changesets are present in your changelog files.

Quick fix

Fixes Liquibase: Change set already exists

<changeSet id="001" author="myauthor">
    <preConditions onFail="MARK_RAN">
        <not>
            <tableExists tableName="my_table" />
        </not>
    </preConditions>
    <createTable tableName="my_table">
        <column name="id" type="INT" />
    </createTable>
</changeSet>
  1. Verify the changeset exists in DATABASECHANGELOG
  2. Use preconditions to prevent duplicate executions
  3. Use validCheckSum for modified changesets
  4. Use runOnChange or runAlways for properties
postgresql β€” error
<changeSet id="001" author="myauthor">
<preConditions onFail="MARK_RAN">
<not>
<tableExists tableName="my_table" />
</not>
</preConditions>
<createTable tableName="my_table">
<column name="id" type="INT" />
</createTable>
</changeSet>
Liquibase: Change set already exists

What this error means

Liquibase tracks all applied migrations in a special DATABASECHANGELOG table. Each changeset is uniquely identified by a combination of the changelog file path, author ID, and changeset ID. When Liquibase encounters a changeset it has already executed, it checks the changeset's content against what's recorded. If the changeset has been modified since it was first applied, or if duplicate identifiers exist, Liquibase raises this error to prevent unpredictable behavior.

Typical symptoms

  • Liquibase migration fails with 'Change set already exists' error
  • Validation errors about duplicate changeset identifiers
  • Database migration stops unexpectedly on second or subsequent runs
  • Changes that worked previously now fail in continuous deployment pipelines
  • Checksum mismatch errors when running `liquibase update`
  • The DATABASECHANGELOG table shows duplicate entries for the same changeset

Common causes

  1. A changeset was modified after it was first applied to the database (content differs, but changeset ID is the same)
  2. Duplicate changeset identifiers exist in your changelog files (same author and ID in different files)
  3. Upgrading PostgreSQL JDBC driver (e.g., 42.7.4 to 42.7.5) causes Liquibase to fail recognizing the existing DATABASECHANGELOG table
  4. Merging conflicting changelog files without resolving duplicate IDs
  5. Using includeAll in Liquibase configuration with overlapping changelog directories
  6. A changeset was manually deleted from the changelog file but was never marked as ignored in Liquibase
  7. Running migrations with uppercase database names in PostgreSQL connection string

How to fix "Liquibase: Change set already exists"

1Verify the changeset exists in DATABASECHANGELOG

Connect to your PostgreSQL database and check if the changeset is already recorded:

```sql
SELECT id, author, filename, exectype, orderexecuted
FROM databasechangelog
WHERE id = 'your_changeset_id' AND author = 'your_author'
ORDER BY orderexecuted DESC;

2Use preconditions to prevent duplicate executions

Add preconditions to your changesets to prevent re-execution of idempotent operations. This is the best practice for complex migrations.

Example XML changelog:

xml
<changeSet id="001" author="myauthor">
    <preConditions onFail="MARK_RAN">
        <not>
            <tableExists tableName="my_table" />
        </not>
    </preConditions>
    <createTable tableName="my_table">
        <column name="id" type="INT" />
    </createTable>
</changeSet>

The onFail="MARK_RAN" tells Liquibase to skip the changeset if the precondition fails, preventing the error.

3Use validCheckSum for modified changesets

If a changeset was modified after deployment and you want Liquibase to accept the new version, add the valid checksum:

bash
liquibase calculate-checksum --changelog-file=db/changelog.xml --changeset-identifier=001::myauthor

Then add this checksum to your changeset:

xml
<changeSet id="001" author="myauthor">
    <validCheckSum>9:c4a3293f78f30058f9144aeb8186beb7</validCheckSum>
    <!-- Rest of your changeset -->
</changeSet>
4Use runOnChange or runAlways for properties

If your changeset uses property substitution that changes between environments, set the runOnChange attribute:

xml
<changeSet id="001" author="myauthor" runOnChange="true">
    <sql>
        UPDATE my_table SET config_value = '${env.config}'
    </sql>
</changeSet>

Alternatively, use runAlways="true" if the changeset should execute every time Liquibase runs.

5Resolve duplicate changeset identifiers

Search your changelog files for duplicate IDs:

bash
grep -r 'id="' db/changelog/ | sort | uniq -d

For each duplicate, rename one of them to have a unique ID:

xml
<!-- Before -->
<changeSet id="001" author="myauthor">
    <!-- changes -->
</changeSet>

<!-- After -->
<changeSet id="001-renamed" author="myauthor">
    <!-- changes -->
</changeSet>
6Handle changelog merges properly

When merging branches with conflicting changelog files:

1. Don't modify existing changeset IDs
2. Append new changesets instead of editing old ones
3. Use a numbering scheme that prevents collisions (e.g., timestamps: id="202501281000")
4. Verify no duplicate IDs exist before committing:

bash
grep -oh 'id="[^"]*"' db/changelog/*.xml | sort | uniq -c | grep -v '^ *1 '
7For PostgreSQL JDBC driver issues (42.7.5+)

If you upgraded the PostgreSQL JDBC driver and now get 'relation already exists' errors:

1. Check your JDBC driver version:
- Current version: 42.7.5+ (check pom.xml or build.gradle)

2. Verify uppercase database names in connection string:

bash
jdbc:postgresql://localhost:5432/MYDB  // Problematic
   jdbc:postgresql://localhost:5432/mydb  // Correct

3. Either:
- Use lowercase database names, OR
- Downgrade to PostgreSQL JDBC 42.7.4 temporarily, OR
- Wait for a Liquibase/driver patch

4. Test with:

bash
liquibase status --db-url=jdbc:postgresql://localhost:5432/mydb
8Use --allow-duplicated-changeset-identifiers (Liquibase 4.25.1+)

If you're on Liquibase 4.25.1 or later and have duplicate changesets that can't be merged immediately:

bash
liquibase update --allow-duplicated-changeset-identifiers=true

This allows Liquibase to deploy multiple changesets with the same ID, but only creates one database changelog entry. Use this as a temporary measure while you work to make IDs unique.

Advanced notes

Checksum Calculation: Liquibase computes an MD5 checksum of each changeset's content. Any whitespace, comment, or formatting change invalidates the checksum, even if the SQL is functionally identical. Use calculate-checksum to debug.

Roll Forward Best Practice: Instead of modifying old changesets, always roll forward with new ones. If you need to rename a table created by changeset 001, create changeset 002 with the rename operation. This maintains audit trail and prevents checksum conflicts.

DATABASECHANGELOG Table: This is the source of truth. If it gets corrupted or out of sync with your changelog files, you'll encounter these errors. In rare cases, a manual SQL fix is needed, but document why and communicate with your team.

Preconditions vs. Error Handling: Use preconditions (recommended) rather than failOnError="false". Preconditions are explicit and saferβ€”they skip changesets gracefully without masking real errors.

CI/CD Pipelines: Always ensure your changelog files are merged and validated before pushing. Use a merge-conflict resolution strategy that enforces unique changeset IDs and prevents manual edits to non-current changesets.

Frequently asked questions

What does "Liquibase: Change set already exists" mean?
Liquibase tracks all applied migrations in a special DATABASECHANGELOG table. Each changeset is uniquely identified by a combination of the changelog file path, author ID, and changeset ID. When Liquibase encounters a changeset it has already executed, it checks the changeset's content against what's recorded.
How do I fix "Liquibase: Change set already exists"?
This error occurs when Liquibase detects a changeset identifier that has already been deployed to your PostgreSQL database. It happens when you try to re-run a migration that Liquibase has marked as executed, or when duplicate changesets are present in your changelog files.
What causes "Liquibase: Change set already exists"?
A changeset was modified after it was first applied to the database (content differs, but changeset ID is the same) Duplicate changeset identifiers exist in your changelog files (same author and ID in different files) Upgrading PostgreSQL JDBC driver (e.g., 42.7.4 to 42.7.5) causes Liquibase to fail recognizing the existing DATABASECHANGELOG table
Is the "Liquibase: Change set already exists" error serious?
Yes. This is a high-severity error that usually blocks work until it is fixed, but the steps above resolve it.

Official resources & further reading