payloadcms/payload · warning · Error

Migration aborted: version__status column not found in ${ver

Error message

Migration aborted: version__status column not found in ${versionsTable} table. This migration should only run on schemas that have NOT yet been migrated to per-locale status. If you've already run this migration, no action is needed.

What it means

Thrown by migrateSqliteLocalizeStatus as a guard when the `version__status` column does not exist on the target versions table. This migration converts a single global status column into per-locale status rows; absence of `version__status` means the schema has already been migrated (or was never on the old schema), so re-running would be a no-op or destructive. The error message explicitly tells you no action is needed.

Source

Thrown at packages/drizzle/src/sqlite/predefinedMigrations/localize-status/index.ts:80

      req,
    })
    locales = filteredLocaleObjects.map((locale) =>
      typeof locale === 'string' ? locale : locale.code,
    )
  }
  payload.logger.info({ msg: `Locales: ${locales.join(', ')}` })

  // Check if versions are enabled in config (skip if not)
  if (!entityConfig.versions) {
    payload.logger.info({
      msg: `Skipping migration for ${collectionSlug ? 'collection' : 'global'}: ${entitySlug} - versions not enabled`,
    })
    return
  }

  // Validate that version__status column exists before proceeding
  if (!(await columnExists({ columnName: 'version__status', db, tableName: versionsTable }))) {
    throw new Error(
      `Migration aborted: version__status column not found in ${versionsTable} table. ` +
        `This migration should only run on schemas that have NOT yet been migrated to per-locale status. ` +
        `If you've already run this migration, no action is needed.`,
    )
  }

  const localesTableExists = await tableExists({ db, tableName: localesTable })

  if (!localesTableExists) {
    // SCENARIO 1: Create the locales table (first localized field in versions)
    payload.logger.info({ msg: `Creating new locales table: ${localesTable}` })

    await db.run(
      sql.raw(`CREATE TABLE "${localesTable}" (
        id INTEGER PRIMARY KEY,
        _locale TEXT NOT NULL,
        _parent_id INTEGER NOT NULL,
        version__status TEXT,

View on GitHub (pinned to 00c58b35c0)

Solutions

  1. Treat this error as a successful no-op: the schema is already in the per-locale-status state.
  2. Track migration completion (e.g. a flag/migration record) so the script does not re-run on the same entity.
  3. Wrap the call in a try/catch that swallows this specific guard error when you know re-runs are possible.

Example fix

// before
await migrateSqliteLocalizeStatus({ db, payload, collectionSlug: 'posts' })
// after
try {
  await migrateSqliteLocalizeStatus({ db, payload, collectionSlug: 'posts' })
} catch (err) {
  if (!/version__status column not found/.test(err.message)) throw err
  payload.logger.info('localize-status already applied, skipping')
}
Defensive patterns

Strategy: try-catch

Try / catch

try {
  await migrateSqliteLocalizeStatus({ db, payload, collectionSlug })
} catch (err) {
  if (/version__status column not found/.test(err.message)) {
    payload.logger.info('localize-status already applied; skipping')
    return
  }
  throw err
}

Prevention

When it happens

Trigger: Running migrateSqliteLocalizeStatus a second time on an entity that already completed the migration, or against a fresh schema that never had the old `version__status` column.

Common situations: Migration script is not idempotent-tracking and re-runs on every deploy; re-running after a partial failure that already mutated the column.

Related errors


AI-assisted analysis of payloadcms/payload@00c58b35c0 (2026-08-12). Data as JSON: /api/errors/2c66d8b3c6f96b96. Report an issue: GitHub.