knex/knex · error · Error
mssql cannot create constraint with predicate
Error message
mssql cannot create constraint with predicate
What it means
mssql's `unique()` in the table compiler throws when you request BOTH `useConstraint: true` AND a `predicate` in the options object. SQL Server unique constraints cannot carry a `WHERE` predicate (only filtered unique *indexes* can), so Knex rejects the contradictory combination at compile time.
Source
Thrown at lib/dialects/mssql/schema/mssql-tablecompiler.js:299
*
* @param {string | string[]} columns
* @param {string | {indexName: undefined | string, deferrable?: 'not deferrable'|'deferred'|'immediate', useConstraint?: true|false, predicate?: QueryBuilder }} indexName
*/
unique(columns, indexName) {
/** @type {string | undefined} */
let deferrable;
let useConstraint = false;
let predicate;
if (isObject(indexName)) {
({ indexName, deferrable, useConstraint, predicate } = indexName);
}
if (deferrable && deferrable !== 'not deferrable') {
this.client.logger.warn(
`mssql: unique index [${indexName}] will not be deferrable ${deferrable} because mssql does not support deferred constraints.`
);
}
if (useConstraint && predicate) {
throw new Error('mssql cannot create constraint with predicate');
}
indexName = indexName
? this.formatter.wrap(indexName)
: this._indexCommand('unique', this.tableNameRaw, columns);
if (!Array.isArray(columns)) {
columns = [columns];
}
if (useConstraint) {
// mssql supports unique indexes and unique constraints.
// unique indexes cannot be used with foreign key relationships hence unique constraints are used instead.
this.pushQuery(
`ALTER TABLE ${this.tableName()} ADD CONSTRAINT ${indexName} UNIQUE (${this.formatter.columnize(
columns
)})`
);
} else {View on GitHub (pinned to e25d54bcb7)
Solutions
- For a filtered/partial unique rule on mssql, use a unique *index* with a WHERE filter: drop `useConstraint` and keep `predicate` (Knex emits `CREATE UNIQUE INDEX ... WHERE ...`).
- If you specifically need a constraint (e.g. for FK relationships), remove the `predicate` — constraints cannot be filtered on SQL Server.
- Express soft-delete uniqueness via a computed column (e.g. a `NULL`-able filtered unique index on a computed expression).
- Branch migration DDL by dialect for partial-unique rules.
Example fix
// before
knex.schema.alterTable('users', t => t.unique('email', { useConstraint: true, predicate: knex.raw('WHERE deleted_at IS NULL') }));
// after (filtered unique index, not a constraint)
knex.schema.alterTable('users', t => t.unique('email', { indexName: 'uq_users_email_active', predicate: knex.raw('WHERE deleted_at IS NULL') })); Defensive patterns
Strategy: validation
Validate before calling
function buildUnique(t, columns, opts = {}) {
if (opts.useConstraint && opts.predicate) {
throw new Error('mssql cannot create a constraint with a predicate; use a unique index instead');
}
return t.unique(columns, opts);
} Type guard
function isValidMssqlUniqueOptions(o) { return !(o && o.useConstraint && o.predicate); } Prevention
- For partial uniqueness on mssql, use a unique index (omit useConstraint), not a constraint.
- Drop the predicate when useConstraint is required.
- Branch unique DDL by dialect in migrations.
When it happens
Trigger: Calling `.unique('col', { useConstraint: true, predicate: knex.raw('...') })` or the option-object form `{ indexName, useConstraint: true, predicate: ... }`; migrating a postgres partial unique constraint to mssql verbatim.
Common situations: Partial unique constraints (e.g. 'unique email where active = 1') ported from postgres to SQL Server; generic migration generators that always set `useConstraint` plus a predicate; conditional uniqueness needed for soft-delete patterns.
Related errors
- rename column of views is not supported by this dialect.
- change default values of views is not supported by this dial
- The query is empty
- The binding value ${binding} must be a decimal number.
- Bigint must be safe integer or must be passed as string, saw
AI-assisted analysis of knex/knex@e25d54bcb7 (2026-08-03).
Data as JSON: /data/errors/e3583ab12f6364ee.json.
Report an issue: GitHub.