payloadcms/payload · error · APIError

Collection config for ${join.field.collection.toString()} wa

Error message

Collection config for ${join.field.collection.toString()} was not found

What it means

Thrown while iterating individual joins when the join's target collection (`join.field.collection`) is not a key of `adapter.payload.collections`. The join points at a collection that is not registered at runtime, so no target config/model can be resolved.

Source

Thrown at packages/db-mongodb/src/utilities/buildJoinAggregation.ts:279

    if (!joinsList) {
      throw new APIError(`Failed to retrieve array of joins for ${slug} in collectio ${collection}`)
    }

    for (const join of joinsList) {
      const projectionPath = versions ? `version.${join.joinPath}` : join.joinPath
      if (projection && !projection[projectionPath]) {
        continue
      }

      if (joins?.[join.joinPath] === false) {
        continue
      }

      const collectionConfig = adapter.payload.collections[join.field.collection as string]?.config

      if (!collectionConfig) {
        throw new APIError(
          `Collection config for ${join.field.collection.toString()} was not found`,
        )
      }

      let JoinModel: CollectionModel | undefined

      const useDrafts = (draftsEnabled || versions) && hasDraftsEnabled(collectionConfig)

      if (useDrafts) {
        JoinModel = adapter.versions[collectionConfig.slug]
      } else {
        JoinModel = adapter.collections[collectionConfig.slug]
      }

      if (!JoinModel) {
        throw new APIError(`Join Model was not found for ${collectionConfig.slug}`)
      }

View on GitHub (pinned to 00c58b35c0)

Solutions

  1. Check the join field's `collection` value matches a registered collection slug.
  2. Update or remove stale join definitions referencing deleted collections.
  3. Ensure the target collection/plugin is loaded before/alongside the source.
  4. Search your config for `collection:` references to confirm they all resolve.

Example fix

// before
{ name: 'author', type: 'join', collection: 'users' } // 'Users' is the real slug
// after
{ name: 'author', type: 'join', collection: 'Users' }
Defensive patterns

Strategy: validation

Validate before calling

function assertJoinTarget(payload, targetSlug) {
  if (!payload.collections[targetSlug]) throw new Error(`Join target not registered: ${targetSlug}`)
}

Type guard

const isRegisteredJoinTarget = (payload, target) =>
  typeof target === 'string' && Boolean(payload.collections[target])

Try / catch

try { await payload.find({ collection, joins }) }
catch (e) { if (/Collection config for .* was not found/.test(e.message)) auditJoinTargets(collection) else throw e }

Prevention

When it happens

Trigger: A `join` field whose `collection` value references a slug that doesn't exist — typo, the target collection was deleted/renamed, or the target is provided by a plugin that isn't loaded.

Common situations: Removing/renaming a collection that another collection joins into without updating join definitions; cross-plugin joins where the target plugin is not registered; slug casing mismatch.

Related errors


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