twentyhq/twenty · critical · Error

BackfillMetadataOverrides: "isActive" changed on "core"."${t

Error message

BackfillMetadataOverrides: "isActive" changed on "core"."${table}" (${activeCountBefore} -> ${activeCountAfter}), aborting.

What it means

Thrown by the 2.19 slow instance command that backfills the overrides column from standardOverrides when the count of active rows changes between the before- and after-UPDATE measurements. The migration is designed to be pure data movement (copying standardOverrides into overrides for null overrides rows), so any change in active row count means concurrent activation/deactivation happened during the run, and the migration aborts to avoid an inconsistent result.

Source

Thrown at packages/twenty-server/src/database/commands/upgrade-version-command/2-19/2-19-instance-command-slow-1782986476000-backfill-metadata-overrides.ts:23

const TABLES = ['objectMetadata', 'fieldMetadata'] as const;

@RegisteredInstanceCommand('2.19.0', 1782986476000, { type: 'slow' })
export class BackfillMetadataOverridesSlowInstanceCommand
  implements SlowInstanceCommand
{
  async runDataMigration(dataSource: DataSource): Promise<void> {
    for (const table of TABLES) {
      const activeCountBefore = await this.getActiveCount(dataSource, table);

      await dataSource.query(
        `UPDATE "core"."${table}" SET "overrides" = "standardOverrides" WHERE "standardOverrides" IS NOT NULL AND "overrides" IS NULL`,
      );

      const activeCountAfter = await this.getActiveCount(dataSource, table);

      if (activeCountBefore !== activeCountAfter) {
        throw new Error(
          `BackfillMetadataOverrides: "isActive" changed on "core"."${table}" (${activeCountBefore} -> ${activeCountAfter}), aborting.`,
        );
      }
    }
  }

  public async up(_queryRunner: QueryRunner): Promise<void> {
    return;
  }

  public async down(_queryRunner: QueryRunner): Promise<void> {
    return;
  }

  private async getActiveCount(
    dataSource: DataSource,
    table: (typeof TABLES)[number],
  ): Promise<number> {

View on GitHub (pinned to 1f5dd2bbd2)

Solutions

  1. Run slow instance migrations in a maintenance window with the API server stopped or with workspace mutations paused.
  2. Re-run the slow instance command after the concurrency source is gone; the UPDATE is idempotent for null-overrides rows.
  3. If the count drift is persistent, audit for stale workspace-deactivation jobs or scheduled cleanups that flip isActive.
  4. Confirm only one upgrade runner is active (check for orphaned runners).

Example fix

// before: slow migration runs while API serves traffic
// after: stop API, run migration, restart
// process: yarn workspace twenty-server stop && npx nx run twenty-server:database:migrate:prod && yarn workspace twenty-server start
Defensive patterns

Strategy: validation

Validate before calling

// Preflight: confirm no concurrent process flips isActive during the slow command.
// Pause API / workspace provisioning, then assert counts are stable across two reads.
const before = await dataSource.query(`SELECT count(*)::int AS c FROM core."objectMetadata" WHERE "isActive" = true`);
await sleep(2000);
const after = await dataSource.query(`SELECT count(*)::int AS c FROM core."objectMetadata" WHERE "isActive" = true`);
if (before[0].c !== after[0].c) {
  throw new Error('isActive churn detected; run slow migration in a maintenance window');
}

Try / catch

// The guard is built into the migration itself. Re-run after quiescing traffic.
try {
  await instanceMigration.runDataMigration(dataSource);
} catch (err) {
  if (err instanceof Error && err.message.startsWith('BackfillMetadataOverrides:')) {
    // quiesce traffic, then re-run; the UPDATE is idempotent for null-overrides rows
    await waitForQuietPeriod();
    throw err;
  }
  throw err;
}

Prevention

When it happens

Trigger: Concurrent writes to core.objectMetadata or core.fieldMetadata during the slow instance migration that flip isActive on any row; another upgrade process, a workspace creation, or an object/field delete operation running in parallel with the slow command's runDataMigration.

Common situations: Running the slow instance migration while the API server is still serving traffic that creates/deactivates objects or fields; running two upgrade runners at once; long-running data migration overlapped with active workspace provisioning.

Related errors


AI-assisted analysis of twentyhq/twenty@1f5dd2bbd2 (2026-08-12). Data as JSON: /api/errors/7ad70c4c2e5e1656. Report an issue: GitHub.