Docs/Reference/Troubleshooting
OSS

Troubleshooting

Common failures, checked against the current CLI. Paid linting is called out where it applies.

Can't connect

  1. Confirm the process can reach the database.
  2. Check database.url / DBLIFT_DB_URL / --db-url. A stray DBLIFT_DB_URL beats YAML.
  3. Then:
dblift db validate-config
dblift db diagnose-connection
dblift db check-connection
dblift db list-drivers

check-connection is the connection test. There is no db test-connection.

A role that connects but lacks a privilege is reported with the database's own message (PostgreSQL permission denied for schema …, for example), not as Connection failed: invalid credentials. An authentication failure still uses the credentials wording.

A missing extra shows up as an import error at connect time, not at pip install dblift. Install the extra that matches database.type — dblift[postgresql], dblift[mysql], dblift[mariadb], dblift[oracle], dblift[sqlserver], dblift[db2], dblift[duckdb], dblift[cosmosdb], dblift[mongodb], dblift[redshift], dblift[snowflake], or the matching PostgreSQL-family extra. See the capability matrix. There is no dblift[sqlite] extra; SQLite uses the stdlib.

The wrong environment ran

Environment name selection is --env (or the API keyword) → DBLIFT_ENV (or resolve.env_var) → resolve.branch_map matched against resolve.branch_var. An unknown name fails fast and lists configured names — including when no environments: section exists.

Value merge is CLI args → env vars → file → defaults. Named-environment YAML is merged into the file layer before that.

dblift info after a migrate I already ran

Re-running migrate does not error with "already applied". Pending scripts run; applied ones are skipped. Use dblift info to see state. Common display values: Success, Pending, Failed, Out of order, Undone. Others exist (Baseline, Missing, Outdated, Needs repair, …).

To change an applied version, add a new versioned file. Do not edit an applied script unless you intend a checksum mismatch.

Out of order

A versioned file whose version is lower than the highest applied version is still applied by default. migrate logs a warning and continues. dblift info then labels that row Out of order.

--strict / strict_mode: true refuses that on migrate and validate: pending versioned scripts must not sit below the highest applied version, and every successful applied row must still have a script on disk (baselines and undo rows excluded). migrate --strict raises StrictModeError and tells you to renumber or rerun without --strict.

After the fact, without strict mode: rename the file to a newer version, or skip execution with dblift migrate --mark-as-executed --versions 1.0.5 only when the change is already in the database.

Undo did nothing / refused

undo runs the paired U<version>__*.sql (or .py) newest-first. A versioned migration with no undo companion cannot be reversed. Preview with dblift undo --target-version 1.0.0 --dry-run --show-sql.

Special characters in SQL files

yaml
migrations: script_encoding: "utf-8" detect_encoding: true # only for mixed/unknown legacy files

Default is strict script_encoding (utf-8). Decode failure stops the run; characters are not silently corrupted.

Failed migrate

  1. dblift info — look at the failed row.
  2. Read the command log. The result may set failed_history_persisted.
  3. If that flag is false, the history table may not contain the failure; use DB logs plus the DBLift log.
  4. On auto-commit engines (MySQL, MariaDB, Oracle, YugabyteDB, Snowflake) or document stores, inspect objects that already landed.
  5. dblift repair only after you decide whether the failed or missing row should be reconciled.

The in-process statement journal enriches command results during a run only. It is not written to disk. Durable evidence is the command log (text / json / html), dblift_schema_history, and database logs.

Lock held

Another migrate may still be running. Check the process list, then native locks (PostgreSQL advisory, SQL Server application, MySQL named, Oracle/DB2, Cosmos lock documents). Table fallback: dblift_migration_lock in the target schema — delete a row only when its owner is confirmed dead. Then dblift info before retrying migrate.

History out of sync

dblift validate
dblift repair

repair rewrites history metadata (checksums, failed rows, missing/extra rows). It does not mutate schema objects.

If the schema-history table cannot be created — a reader role on an empty schema — validate errors with ConnectionError (Could not create the schema-history table: ...) instead of returning a validation result. --format json is {"success": false, "error": "ConnectionError: ..."}. error is null on a successful validate, not "". Missing applied files are reported only with --strict, and only when the history table already has applied rows.

Start over (dev only)

dblift clean --dry-run
dblift clean --clean-enabled

clean is disabled unless --clean-enabled (or config) turns it on. It drops managed objects including the history table.

SQL failed to parse or run

  • History / checksum / ordering: dblift validate (needs the configured database).
  • Offline rule lint of .sql files: dblift validate-sql (Pro command; packs and profiles need Enterprise).
  • Test the statement in a client for the same dialect.

Python migrations are not linted by validate-sql. validate is not a SQL parser.

Document store: DBLIFT-NOSQL-001

A .sql file against Cosmos DB or MongoDB fails before execute:

DBLIFT-NOSQL-001: '<file>.sql' is a SQL migration, but the '<dialect>' dialect
does not execute SQL migrations.

Rewrite as V1_0_0__….py and drive context.db / context.raw_client. See Python migrations.

Already-applied .sql history rows stay valid if you leave the file in place. Cosmos pseudo-SQL (DROP CONTAINER, SET THROUGHPUT, …) is not translated.

On Cosmos DB, NoSqlWriteNotSupportedError means a Python migration passed a write to context.execute(); only native SELECT runs there. An AttributeError on context.database or context.client means the script predates the rename to context.db / context.raw_client.

MongoDB: DBLIFT-NOSQL-002

context.execute(...) always fails on MongoDB (no string query language). Use context.db["users"].find(...) or collection write methods.

Cosmos / Mongo connection

Cosmos: endpoint + key or managed identity; local emulator is typically port 8081.

MongoDB: url (mongodb:// / mongodb+srv://) or host/port; database is required even when url is set. Atlas often needs authSource=admin in the URI. Local default port is 27017.

SQLite path

Use an absolute path or a path relative to the working directory. The directory must exist. Three slashes is relative (sqlite:///./app.db); four slashes is absolute (sqlite:////abs/path/app.db).

Multiple directories / tags

yaml
migrations: directories: - ./migrations/core - ./migrations/features

Paths are relative to the config file. dblift info lists what was discovered.

Tags live in the filename: V1_0_0__migration[tag1,tag2].sql. Matching is case-sensitive. Filter with --tags tag1,tag2.

SQL Server notes

Full-text CREATE FULLTEXT CATALOG / CREATE FULLTEXT INDEX cannot share a transaction with ordinary DDL — put them in their own versioned file. Mixing them fails with:

Error: Migration V1__create_schema.sql mixes transactional and autocommit-on SQL

Unqualified DDL follows the login DEFAULT_SCHEMA, which is catalog state shared across connections. DBLift sets it with ALTER USER ... WITH DEFAULT_SCHEMA and warns if another session changed it. Use one login per --db-schema.

Next

On this page