payloadcms/payload · error · APIError
Collection ${collectionSlug} not found
Error message
Collection ${collectionSlug} not found What it means
Thrown by the R2 storage adapter's default client-upload access callback (defaultR2ClientUploadsAccess). It looks up the requested collection slug in req.payload.collections (the Payload-registered collections) and fires a bare APIError (HTTP 500 by default) when the slug is absent or has no config. This guards the multipart-upload endpoint before any create-access policy is evaluated.
Source
Thrown at packages/storage-r2/src/handleMultiPartUpload.ts:22
import { APIError, Forbidden } from 'payload'
import type { R2StorageOptions } from './index.js'
import type { R2Bucket, R2StorageMultipartUploadHandlerParams } from './types.js'
type Args = {
access?: UploadInstructionsAccess
bucket: R2Bucket
collections: R2StorageOptions['collections']
useCompositePrefixes?: boolean
}
export const defaultR2ClientUploadsAccess: UploadInstructionsAccess = async ({
collectionSlug,
req,
}) => {
const collection = req.payload.collections[collectionSlug]
if (!collection?.config) {
throw new APIError(`Collection ${collectionSlug} not found`)
}
const createAccess = collection.config.access?.create
return createAccess
? Boolean(await createAccess({ slug: collectionSlug, req }))
: Boolean(req.user)
}
// Adapted from https://developers.cloudflare.com/r2/api/workers/workers-multipart-usage/
export const getHandleMultiPartUpload =
({
access = defaultR2ClientUploadsAccess,
bucket,
collections,
useCompositePrefixes = false,
}: Args): PayloadHandler =>
async (req) => {
const params = Object.fromEntries(req.searchParams) as R2StorageMultipartUploadHandlerParamsView on GitHub (pinned to 00c58b35c0)
Solutions
- Verify the ?collection query parameter on the failing request exactly matches a slug in payload.config.collections.
- Check that the collection is actually registered (not commented out / conditionally excluded) in the Payload config loaded by the worker/server.
- If you supply a custom access function via the R2 adapter's `access` option, validate collectionSlug against req.payload.collections yourself before throwing, or return a clearer error.
- Confirm the collection's config object is defined (collection?.config) — a partial registration can leave the entry without a config.
Example fix
// before
const collection = req.payload.collections[collectionSlug]
if (!collection?.config) {
throw new APIError(`Collection ${collectionSlug} not found`)
}
// after — explicit 404-style response with the registered slugs for debugging
const collection = req.payload.collections[collectionSlug]
if (!collection?.config) {
throw new APIError(
`Collection '${collectionSlug}' not found. Registered: ${Object.keys(req.payload.collections).join(', ')}`,
404,
)
} Defensive patterns
Strategy: validation
Validate before calling
import type { CollectionSlug } from 'payload'
function isValidCollectionSlug(slug: string, config: { collections: Record<string, unknown> }): slug is CollectionSlug {
return Boolean(config.collections[slug])
}
// before issuing the multipart request
if (!isValidCollectionSlug(collectionSlug, payloadConfig)) {
throw new Error(`Refusing upload: '${collectionSlug}' is not a registered collection`)
} Type guard
function isRegisteredCollection(
slug: string,
collections: Record<string, { config?: unknown }>,
): slug is string {
return Boolean(collections[slug]?.config)
} Try / catch
try {
await initiateMultipartUpload({ collection: slug })
} catch (err) {
if (err instanceof APIError && /not found/i.test(err.message)) {
// surface a user-friendly 'collection unavailable' message
}
throw err
} Prevention
- Derive the collection slug from the registered Payload config on the client rather than hardcoding it.
- Keep a single source of truth for collection slugs shared between client and server.
- Add a startup assertion that every storage-adapter collection slug exists in payload.config.collections.
When it happens
Trigger: A multipart upload POST hits the R2 handler with ?collection=<slug> where <slug> is not a key in req.payload.collections (typo, unregistered collection, or a slug only configured on the storage adapter but not registered as a Payload collection).
Common situations: Client sends a collection slug that differs from the server's registered slug (casing, pluralization); the collection was removed/disabled in config but the client still references it; a custom upload UI hardcodes a stale slug; multi-tenant setups where the slug is dynamically constructed and sometimes empty.
Related errors
- Collection ${collectionSlug} was not found in R2 Storage opt
- You are not allowed to perform this action.
- Collection ${args.collection.config.slug} has disabled bulk
- storage contains an invalid entry: expected an object with a
- Field ${field.label} has reserved name '${fieldName}'.
AI-assisted analysis of payloadcms/payload@00c58b35c0 (2026-08-12).
Data as JSON: /api/errors/359d01fbcdcac6b8.
Report an issue: GitHub.