payloadcms/payload · error · APIError

Either collection or globalSlug must be passed.

Error message

Either collection or globalSlug must be passed.

What it means

Thrown by the Drizzle `deleteVersions` when neither `collectionSlug` nor `globalSlug` is supplied — both the global branch and the collection branch are skipped, so the method cannot determine which versions table to delete from and aborts. This is primarily an internal-contract violation since `deleteVersions` is an adapter method, not a public API.

Source

Thrown at packages/drizzle/src/deleteVersions.ts:32

  this: DrizzleAdapter,
  { collection: collectionSlug, globalSlug, locale, req, where: where },
) {
  let tableName: string
  let fields: FlattenedField[]

  if (globalSlug) {
    const globalConfig = this.payload.globals.config.find(({ slug }) => slug === globalSlug)
    tableName = this.tableNameMap.get(`_${toSnakeCase(globalSlug)}${this.versionsSuffix}`)
    fields = buildVersionGlobalFields(this.payload.config, globalConfig, true)
  } else if (collectionSlug) {
    const collectionConfig: SanitizedCollectionConfig =
      this.payload.collections[collectionSlug].config
    tableName = this.tableNameMap.get(
      `_${toSnakeCase(collectionConfig.slug)}${this.versionsSuffix}`,
    )
    fields = buildVersionCollectionFields(this.payload.config, collectionConfig, true)
  } else {
    throw new APIError('Either collection or globalSlug must be passed.')
  }

  const { docs } = await findMany({
    adapter: this,
    fields,
    joins: false,
    limit: 0,
    locale,
    page: 1,
    pagination: false,
    req,
    tableName,
    where,
  })

  const ids = []

  docs.forEach((doc) => {

View on GitHub (pinned to 00c58b35c0)

Solutions

  1. Always pass `collectionSlug` or `globalSlug` when calling `deleteVersions`.
  2. Prefer the public API (`payload.deleteVersions({ collection })` / `{ global }`) over adapter internals.
  3. If hitting this via a public API path, report it as a bug with a reproduction.
  4. In custom subclasses, forward both slugs from the caller.

Example fix

// before
await adapter.deleteVersions({ req }) // no slug
// after
await payload.deleteVersions({ collection: 'posts', id, req })
Defensive patterns

Strategy: validation

Validate before calling

function assertDeleteVersionsTarget(args) {
  if (!args.collectionSlug && !args.globalSlug)
    throw new Error('deleteVersions requires collectionSlug or globalSlug')
}

Type guard

const hasDeleteTarget = (a) => Boolean(a?.collectionSlug) || Boolean(a?.globalSlug)

Try / catch

try { await adapter.deleteVersions(args) }
catch (e) { if (/Either collection or globalSlug/.test(e.message)) throw new Error('Caller must specify a target') else throw e }

Prevention

When it happens

Trigger: An internal/programmatic call to the adapter's `deleteVersions` without specifying a target slug — e.g. a custom adapter subclass, a fork calling private methods, or a regression in Payload internals that drops the slug.

Common situations: Custom adapter subclassing that overrides/wraps deleteVersions; calling adapter internals directly instead of the public API; an upgrade regression that stopped forwarding the slug.

Related errors


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