drizzle-team/drizzle-orm · error · Error

The '${table1.name}' and '${table2.name}' tables have not nu

Error message

The '${table1.name}' and '${table2.name}' tables have not null foreign keys. You can't seed cyclic tables with not null foreign key columns.

What it means

When two tables reference each other cyclically and BOTH sides of the cycle have NOT NULL foreign-key columns, drizzle-seed cannot insert either table first: each row requires a valid non-null FK value from the other table that does not yet exist. The cyclicTablesCompare comparator detects this deadlock and throws.

Source

Thrown at drizzle-seed/src/services/SeedService.ts:356

	cyclicTablesCompare = (
		table1: Table,
		table2: Table,
		relation: Relation & { isCyclic: boolean },
		reverseRelation: Relation & { isCyclic: boolean } | undefined,
	) => {
		// TODO: revise
		const hasTable1NotNullColumns = relation.columns.some((colIName) =>
			table1.columns.find((colJ) => colJ.name === colIName)?.notNull === true
		);

		if (reverseRelation !== undefined) {
			const hasTable2NotNullColumns = reverseRelation.columns.some((colIName) =>
				table2.columns.find((colJ) => colJ.name === colIName)?.notNull === true
			);

			if (hasTable1NotNullColumns && hasTable2NotNullColumns) {
				throw new Error(
					`The '${table1.name}' and '${table2.name}' tables have not null foreign keys. You can't seed cyclic tables with not null foreign key columns.`,
				);
			}

			if (hasTable1NotNullColumns) return 1;
			else if (hasTable2NotNullColumns) return -1;
			return 0;
		}

		if (hasTable1NotNullColumns) {
			return 1;
		}
		return 0;

		// if (hasTable1NotNullColumns) return 1;
		// else if (hasTable2NotNullColumns) return -1;
	};

View on GitHub (pinned to b7862528fd)

Solutions

  1. Make at least one side of the cycle nullable so drizzle-seed can insert that table first, back-fill, then update.
  2. Use database-level deferred FK constraints (Postgres DEFERRABLE INITIALLY DEFERRED) and set the column nullable for seeding.
  3. Break the cycle by seeding one table with a refined generator that supplies a placeholder/valid value, then seed the other.

Example fix

// before — mutual NOT NULL FKs
users: { profileId: integer().notNull().references(() => profiles.id) }
profiles: { userId: integer().notNull().references(() => users.id) }
// after — make one side nullable
users: { profileId: integer().references(() => profiles.id) }
Defensive patterns

Strategy: validation

Validate before calling

// Detect mutual NOT NULL FK cycles before seeding.
function hasMutualNotNullCycle(
  relations: Array<{ table: string; refTable: string; columns: string[]; isCyclic: boolean }>,
  columnNotNull: (table: string, col: string) => boolean
): boolean {
  for (const r of relations) {
    if (!r.isCyclic) continue;
    const reverse = relations.find((x) => x.table === r.refTable && x.refTable === r.table);
    if (!reverse) continue;
    const sideA = r.columns.some((c) => columnNotNull(r.table, c));
    const sideB = reverse.columns.some((c) => columnNotNull(reverse.table, c));
    if (sideA && sideB) return true;
  }
  return false;
}

Prevention

When it happens

Trigger: Two tables A and B where A has a NOT NULL FK to B and B has a NOT NULL FK to A (mutual mandatory dependency); the seeder needs to order them but neither can go first.

Common situations: Modeling bidirectional relationships with mandatory links (e.g. User.profileId NOT NULL and Profile.userId NOT NULL); schema where a deferred/nullable FK was accidentally made NOT NULL.

Related errors


AI-assisted analysis of drizzle-team/drizzle-orm@b7862528fd (2026-08-03). Data as JSON: /data/errors/cb9299b4449504ef.json. Report an issue: GitHub.