payloadcms/payload · error · Error

Migration aborted: version._status field not found or has un

Error message

Migration aborted: version._status field not found or has unexpected format in ${versionsCollection}. This migration should only run on schemas that have NOT yet been migrated to per-locale status.

What it means

Before migrating, localizeStatus samples one version document and requires version._status to be a plain string (the pre-migration shape). If version._status is missing, an array, or otherwise non-string, the migration aborts to avoid corrupting data. This protects against running on already-migrated or schema-drifted data.

Source

Thrown at packages/db-mongodb/src/predefinedMigrations/migrateLocalizeStatus.ts:168

  // Check if _status is already localized
  if (
    sampleDoc.version?._status &&
    typeof sampleDoc.version._status === 'object' &&
    !Array.isArray(sampleDoc.version._status)
  ) {
    payload.logger.info({
      msg: 'version._status is already localized, migration already completed',
    })
    return
  }

  // Validate that version._status exists and is a string
  if (
    !sampleDoc.version ||
    typeof sampleDoc.version._status !== 'string' ||
    Array.isArray(sampleDoc.version._status)
  ) {
    throw new Error(
      `Migration aborted: version._status field not found or has unexpected format in ${versionsCollection}. ` +
        `This migration should only run on schemas that have NOT yet been migrated to per-locale status.`,
    )
  }

  payload.logger.info({ msg: 'Fetching all version documents...' })

  // Get all versions, sorted chronologically
  const allVersions = await connection
    .collection(versionsCollection)
    .find({}, { session })
    .sort({ createdAt: 1, parent: 1 })
    .toArray()

  payload.logger.info({ msg: `Found ${allVersions.length} version documents` })

  // Transform MongoDB documents to VersionRecord format
  const versionRecords: VersionRecord[] = allVersions.map((doc: any) => ({

View on GitHub (pinned to 00c58b35c0)

Solutions

  1. If the migration already ran, no action is needed — verify a sample doc shows version._status as an object.
  2. If documents legitimately lack version._status (no drafts), backfill them (e.g. set version._status='draft') or exclude the entity from the migration.
  3. Inspect a sample doc: `db._<slug>_versions.findOne()` and confirm the shape before retrying.
  4. Restore from backup and run the migration once on clean pre-migration data if the collection is in a mixed/half-converted state.

Example fix

// diagnostics — inspect before deciding
// mongosh
db._posts_versions.findOne({}, { 'version._status': 1 })

// if half-converted (mixed string + object), backfill to a clean pre-state or
// restore from backup, then run migrateLocalizeStatus once.
Defensive patterns

Strategy: validation

Validate before calling

// Sample the versions collection before invoking the migration
async function assertPreMigrationShape(db: any, versionsCollection: string) {
  const sample = await db.collection(versionsCollection).findOne({})
  if (!sample) return // nothing to migrate
  if (typeof sample.version?._status !== 'string') {
    throw new Error(`${versionsCollection} is not in pre-migration shape (version._status is ${typeof sample.version?._status})`)
  }
}

Type guard

function isPreMigrationStatus(sample: unknown): sample is { version: { _status: string } } {
  return typeof sample === 'object' && sample !== null &&
    typeof (sample as any)?.version?._status === 'string'
}

Try / catch

try {
  await localizeStatus({ collectionSlug, payload, req })
} catch (e) {
  if (e instanceof Error && e.message.includes('Migration aborted')) {
    payload.logger.warn('localize-status already applied or data not in pre-migration shape — skipping')
    return
  }
  throw e
}

Prevention

When it happens

Trigger: Running localizeStatus on a versions collection whose sample document lacks version._status (e.g. drafts never produced a status), where _status was already converted to an object (re-running the migration), or where custom code wrote an array/non-string into version._status.

Common situations: Re-running the localize-status migration after it already completed; documents created by a custom import that omitted version._status; partial prior migration left the collection half-converted; versions enabled without drafts so _status was never written.

Related errors


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