payloadcms/payload · error · Error

storage contains an invalid entry: expected an object with a

Error message

storage contains an invalid entry: expected an object with an `init` function. Ensure you are passing the result of a storage adapter factory (e.g. s3Storage({…})) to `storage`, not to `plugins`.

What it means

Thrown at config build time when an entry in `config.storage` does not expose an `init` function. Storage adapters must be the result of a factory call (e.g. `s3Storage({…})`) that returns `{ init }`. The check exists because a common mistake is placing the adapter in `plugins` or passing the raw options object instead of the constructed adapter.

Source

Thrown at packages/payload/src/config/build.ts:22

/**
 * @description Builds and validates Payload configuration
 * @param config Payload Config
 * @returns Built and sanitized Payload Config
 */
export async function buildConfig(config: Config): Promise<SanitizedConfig> {
  if (Array.isArray(config.plugins)) {
    const sorted = [...config.plugins].sort((a, b) => (a.order ?? 0) - (b.order ?? 0))

    for (const plugin of sorted) {
      config = await plugin(config)
    }
  }

  if (Array.isArray(config.storage)) {
    for (const adapter of config.storage) {
      if (typeof adapter?.init !== 'function') {
        throw new Error(
          `storage contains an invalid entry: expected an object with an \`init\` function. ` +
            `Ensure you are passing the result of a storage adapter factory (e.g. s3Storage({…})) ` +
            `to \`storage\`, not to \`plugins\`.`,
        )
      }
      config = await adapter.init(config)
    }
  }

  return sanitizeConfig(config)
}

View on GitHub (pinned to 00c58b35c0)

Solutions

  1. Move the adapter from `plugins` into the `storage` array.
  2. Ensure each storage entry is a factory invocation, e.g. `storage: [s3Storage({ bucket: 'x' })]`, not the bare options or the function reference.
  3. If writing a custom adapter, return an object whose `init` is an async `(config) => config`.

Example fix

// before
buildConfig({ plugins: [s3Storage({ bucket: 'x' })] })
// after
import { s3Storage } from '@payloadcms/storage-s3'
buildConfig({ storage: [s3Storage({ bucket: 'x' })] })
Defensive patterns

Strategy: type-guard

Validate before calling

import { buildConfig } from 'payload'
const storage = [s3Storage({ bucket: 'x' })]
if (!Array.isArray(storage) || storage.some((a) => typeof a?.init !== 'function')) {
  throw new Error('Every storage entry must be a factory result with an init() function')
}
await buildConfig({ storage })

Type guard

import type { StorageAdapter } from 'payload'
function isStorageAdapter(a: unknown): a is StorageAdapter {
  return typeof a === 'object' && a !== null && typeof (a as any).init === 'function'
}
// assert: if (!storage.every(isStorageAdapter)) throw

Prevention

When it happens

Trigger: Passing `s3Storage` (the factory) instead of `s3Storage({...})` (its result) to `storage`; pushing a storage adapter into the `plugins` array by mistake; passing `{ bucket: 'x' }` options object directly to `storage`.

Common situations: Migrating from the old upload-adapter API to the v3 `storage` config; copy-paste from a plugin example into the storage slot; forgetting to call the factory.

Related errors


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