knex/knex · error · Error
Transaction concluded with ${foreignViolations.length} forei
Error message
Transaction concluded with ${foreignViolations.length} foreign key violations What it means
When a strict SQLite transaction temporarily disables foreign_keys (the connection had it ON and the caller requested enforceForeignCheck: false), Knex re-enables checking at commit time and runs PRAGMA foreign_key_check. If that returns any violation rows, the transaction is rejected with this error and will be rolled back. This makes 'temporarily relax FK during this transaction' safe-by-construction: data that violates constraints cannot silently escape.
Source
Thrown at lib/dialects/sqlite3/execution/sqlite-transaction.js:112
`Refusing to create transaction: unable to change \`foreign_keys\` pragma inside a nested transaction`
);
}
let maybeWrappedContainer = container;
if (restoreForeignCheck === true) {
// in the case where we are turning foreign key checks off for the duration of a transaction,
// we need to assert that there are no violations once the work of the transaction has been
// completed. this relies on the fact that Transaction._onAcquire runs the "container" promise
// to completion before executing "COMMIT"
maybeWrappedContainer = async (trx) => {
const res = await container(trx);
const foreignViolations = await this.client
.raw(executeForeignCheck())
.connection(conn);
if (foreignViolations.length > 0) {
throw new Error(
`Transaction concluded with ${foreignViolations.length} foreign key violations`
);
}
return res;
};
}
try {
// call out to the base class to actually do the work as it normally would
// note: the await is required here! we need to resolve the promise, not
// return it
return await this._onAcquire(maybeWrappedContainer, conn);
} finally {
// set the foreign_keys pragma back to what it was before we performed the transaction
this._restoreForeignCheck(conn, restoreForeignCheck).catch((e) => {
// we were unable to put it back like we found it. dispose the connection and
// allow any further queries to re-acquire a new, clean connection
this._logAndDispose(View on GitHub (pinned to e25d54bcb7)
Solutions
- Inspect PRAGMA foreign_key_check output for the affected tables and fix the offending rows (missing parents, orphaned children) before retrying.
- Reorder operations so parent rows exist before child rows that reference them.
- Run a pre-flight integrity check (PRAGMA foreign_key_check) on the source data before disabling enforcement.
- If violations are expected and acceptable, do not disable enforcement; instead insert in dependency order under enforceForeignCheck: true.
Example fix
// before
await knex.transaction(async (trx) => {
await trx('child').insert({ parent_id: 999 }); // parent 999 missing
}, { enforceForeignCheck: false });
// after
await trx('parent').insert({ id: 999 });
await trx('child').insert({ parent_id: 999 }); Defensive patterns
Strategy: validation
Validate before calling
// pre-flight: ensure no FK violations exist before disabling enforcement
async function assertNoFkViolations(knex) {
const rows = await knex.raw('pragma foreign_key_check');
if (rows.length) throw new Error('Existing FK violations: ' + JSON.stringify(rows));
}
await assertNoFkViolations(knex);
await knex.transaction(async (trx) => { /* inserts in dependency order */ }, { enforceForeignCheck: false }); Try / catch
try {
await knex.transaction(async (trx) => { /* ... */ }, { enforceForeignCheck: false });
} catch (e) {
if (/foreign key violations/i.test(e.message)) {
const violations = await knex.raw('pragma foreign_key_check');
// repair rows listed in violations, then retry in dependency order
} else throw e;
} Prevention
- Insert parent rows before child rows.
- Run pragma foreign_key_check before and after bulk loads.
- Prefer enforceForeignCheck: true when data integrity is uncertain.
When it happens
Trigger: Issuing knex.transaction(cb, { enforceForeignCheck: false }) (or a DDL alter path that sets enforceForeignCheck to false when not already transacting) and then INSERTing/UPDATEing rows that reference non-existent parent rows, or deleting parent rows that still have dependents. The check fires only when foreign keys were originally enabled and got turned off for the transaction.
Common situations: Bulk imports or seeding that intentionally disable FK checks for speed but contain orphaned references. Reordering dependent/parent inserts so children are inserted before parents. DDL table rebuilds (Knex's SQLite alter strategy) that copy data while FK is off and the data is inconsistent.
Related errors
- Refusing to create an unsafe transaction: client.strictForei
- Refusing to create transaction: unable to change `foreign_ke
- .dropForeignIfExists is not supported for sqlite3
- .dropForeignIfExists is not supported for mssql.
- .dropForeignIfExists is not supported for mysql.
AI-assisted analysis of knex/knex@e25d54bcb7 (2026-08-03).
Data as JSON: /data/errors/71fd8446e5470242.json.
Report an issue: GitHub.