typeorm/typeorm · error · TypeORMError

MySql does not support exclusion constraints.

Error message

MySql does not support exclusion constraints.

What it means

createExclusionConstraint() throws on MySQL because exclusion constraints are a PostgreSQL-specific feature (e.g., EXCLUDE USING GIST) with no MySQL equivalent. The method exists only to satisfy the shared QueryRunner interface.

Source

Thrown at src/driver/mysql/MysqlQueryRunner.ts:2225

    async dropCheckConstraints(
        tableOrName: Table | string,
        checkConstraints: TableCheck[],
        ifExists?: boolean,
    ): Promise<void> {
        throw new TypeORMError(`MySql does not support check constraints.`)
    }

    /**
     * Creates a new exclusion constraint.
     *
     * @param tableOrName
     * @param exclusionConstraint
     */
    async createExclusionConstraint(
        tableOrName: Table | string,
        exclusionConstraint: TableExclusion,
    ): Promise<void> {
        throw new TypeORMError(`MySql does not support exclusion constraints.`)
    }

    /**
     * Creates a new exclusion constraints.
     *
     * @param tableOrName
     * @param exclusionConstraints
     */
    async createExclusionConstraints(
        tableOrName: Table | string,
        exclusionConstraints: TableExclusion[],
    ): Promise<void> {
        throw new TypeORMError(`MySql does not support exclusion constraints.`)
    }

    /**
     * Drops exclusion constraint.
     *

View on GitHub (pinned to 04ff4daedc)

Solutions

  1. Enforce the exclusion rule with a trigger that checks for overlapping rows, or in the application layer with locking.
  2. Keep the constraint on Postgres only by branching on driver type.
  3. Use a generated column + unique index as a partial workaround for simple ranges.

Example fix

// before
await queryRunner.createExclusionConstraint('bookings', new TableExclusion({ name: 'no_overlap', expression: 'USING GIST (room_id WITH =, tstzrange(starts_at, ends_at) WITH &&)' }));

// after (MySQL trigger-based overlap guard)
await queryRunner.query(`CREATE TRIGGER no_overlap_ins BEFORE INSERT ON bookings FOR EACH ROW BEGIN IF EXISTS (SELECT 1 FROM bookings b WHERE b.room_id = NEW.room_id AND NEW.starts_at < b.ends_at AND b.starts_at < NEW.ends_at) THEN SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='overlapping booking'; END IF; END`);
Defensive patterns

Strategy: validation

Validate before calling

import type { QueryRunner } from 'typeorm';
// Enforce an exclusion (e.g. no overlapping bookings) via a trigger:
async function createNoOverlap(runner: QueryRunner) {
  await runner.query(`CREATE TRIGGER no_overlap_ins BEFORE INSERT ON bookings FOR EACH ROW BEGIN IF EXISTS (SELECT 1 FROM bookings b WHERE b.room_id = NEW.room_id AND NEW.starts_at < b.ends_at AND b.starts_at < NEW.ends_at) THEN SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='overlapping booking'; END IF; END`);
}

Type guard

import { InstanceChecker } from 'typeorm';
const isTableExclusion = (v: unknown) => InstanceChecker.isTableExclusion(v);

Try / catch

null

Prevention

When it happens

Trigger: Calling queryRunner.createExclusionConstraint(table, exclusion) on MySQL; reusing Postgres exclusion logic (e.g., preventing overlapping time ranges) on a MySQL backend.

Common situations: Booking/scheduling apps that used EXCLUDE on Postgres being ported to MySQL; shared multi-DB schema code.

Related errors


AI-assisted analysis of typeorm/typeorm@04ff4daedc (2026-08-03). Data as JSON: /data/errors/3adbcb2269db79f5.json. Report an issue: GitHub.