payloadcms/payload · error · Error

Either collectionSlug or globalSlug must be provided

Error message

Either collectionSlug or globalSlug must be provided

What it means

The localizeStatus migration helper requires exactly one entity identifier. Thrown when neither collectionSlug nor globalSlug is passed in LocalizeStatusArgs. This is a programmer-argument guard, not a runtime data condition.

Source

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

  }

  for (const global of globals) {
    await localizeStatus({ globalSlug: global.slug, payload, req, session })
    await cleanupSnapshotDocuments({ connection, entitySlug: global.slug, payload, session })
  }

  payload.logger.info({ msg: 'localize-status migration completed successfully' })
}

/**
 * Converts version._status (and the main document _status) from a scalar string to a
 * per-locale object for a single collection or global.
 */
export async function localizeStatus(args: LocalizeStatusArgs): Promise<void> {
  const { collectionSlug, globalSlug, payload, req, session } = args

  if (!collectionSlug && !globalSlug) {
    throw new Error('Either collectionSlug or globalSlug must be provided')
  }

  if (collectionSlug && globalSlug) {
    throw new Error('Cannot provide both collectionSlug and globalSlug')
  }

  const entitySlug = collectionSlug || globalSlug
  // MongoDB collection names are case-insensitive and stored as lowercase
  const versionsCollection = `_${entitySlug}_versions`.toLowerCase()

  if (!payload.config.localization) {
    throw new Error('Localization is not enabled in payload config')
  }

  // Check if versions are enabled on this collection/global
  let entityConfig
  if (collectionSlug) {
    const collection = payload.config.collections.find((c) => c.slug === collectionSlug)

View on GitHub (pinned to 00c58b35c0)

Solutions

  1. Pass exactly one of collectionSlug or globalSlug when calling localizeStatus.
  2. Prefer the higher-level migrateLocalizeStatus() orchestrator which derives slugs from config automatically.
  3. Add a unit test asserting your caller always provides a slug.
  4. Type the caller so omitting both is a compile error (LocalizeStatusArgs already does this weakly).

Example fix

// before
await localizeStatus({ payload, req })

// after
await localizeStatus({ collectionSlug: 'posts', payload, req })
Defensive patterns

Strategy: validation

Validate before calling

function assertLocalizeTarget(args: { collectionSlug?: string; globalSlug?: string }) {
  if (!args.collectionSlug && !args.globalSlug) {
    throw new Error('localizeStatus requires collectionSlug or globalSlug')
  }
}

Type guard

type LocalizeArgs = { collectionSlug?: string; globalSlug?: string }
function hasOneEntity(a: LocalizeArgs): a is { collectionSlug: string } | { globalSlug: string } {
  return Boolean(a.collectionSlug) !== Boolean(a.globalSlug)
}

Prevention

When it happens

Trigger: Calling localizeStatus() directly (outside the orchestrator migrateLocalizeStatus) with an args object where both collectionSlug and globalSlug are undefined.

Common situations: A custom migration or script that invokes localizeStatus without an entity slug; refactoring that dropped the slug argument.

Related errors


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