payloadcms/payload · critical · Error
Error: missing secret key. A secret key is needed to secure
Error message
Error: missing secret key. A secret key is needed to secure Payload.
What it means
Thrown right after the config resolves, when `this.config.secret` is falsy. Payload derives its encryption keyring (and the active legacy session key) from the secret, so an empty/undefined secret makes sessions, cookies, and field encryption insecure or non-functional. This is a deliberate security guard.
Source
Thrown at packages/payload/src/index.ts:923
process.env.NODE_ENV !== 'production' &&
process.env.PAYLOAD_DISABLE_DEPENDENCY_CHECKER !== 'true' &&
!checkedDependencies
) {
checkedDependencies = true
void checkPayloadDependencies()
}
this.importMap = options.importMap!
if (!options?.config) {
throw new Error('Error: the payload config is required to initialize payload.')
}
this.config = await options.config
this.logger = getLogger('payload', this.config.logger)
if (!this.config.secret) {
throw new Error('Error: missing secret key. A secret key is needed to secure Payload.')
}
this.encryptionKeyring = buildEncryptionKeyring([
this.config.secret,
...(this.config.previousSecrets ?? []),
])
this.secret = this.encryptionKeyring.active.legacyKey
this.globals = {
config: this.config.globals,
}
for (const collection of this.config.collections) {
let customIDType: string | undefined = undefined
const findCustomID: TraverseFieldsCallback = ({ field }) => {
if (
['array', 'blocks', 'group'].includes(field.type) ||
(field.type === 'tab' && 'name' in field)View on GitHub (pinned to 00c58b35c0)
Solutions
- Set a `secret` directly in buildConfig: `secret: process.env.PAYLOAD_SECRET`.
- Ensure `PAYLOAD_SECRET` (32+ chars) is present in every environment (dev, CI, staging, prod).
- Add a startup assertion that the secret is non-empty before init, to fail with a clearer message.
Example fix
// before
export default buildConfig({ /* secret missing */ })
// after
export default buildConfig({
secret: process.env.PAYLOAD_SECRET,
}) Defensive patterns
Strategy: validation
Validate before calling
const secret = process.env.PAYLOAD_SECRET
if (!secret || secret.length < 32) {
throw new Error('PAYLOAD_SECRET must be set and at least 32 chars')
}
await payload.init({ config, secret }) Type guard
function isValidSecret(value: string | undefined): value is string {
return typeof value === 'string' && value.length >= 32
}
if (!isValidSecret(process.env.PAYLOAD_SECRET)) {
throw new Error('Missing or weak PAYLOAD_SECRET')
} Try / catch
try {
await payload.init({ config, secret })
} catch (err) {
if (err instanceof Error && err.message.includes('missing secret key')) {
// set PAYLOAD_SECRET in this environment and restart
}
throw err
} Prevention
- Set PAYLOAD_SECRET in all environments (dev, CI, staging, prod) via your secret manager.
- Add a pre-init assertion on secret presence and length.
- Document the env var in the project README and CI config template.
When it happens
Trigger: Initializing Payload with a config whose `secret` is undefined, empty string, or null — typically because the env var backing it was not set in the current environment.
Common situations: Deploying without `PAYLOAD_SECRET` (or your custom env var); a `.env` file not loaded in the deployment; secret referenced via `process.env.PAYLOAD_SECRET` but the variable name differs; CI/preview environment missing the secret.
Related errors
- Error: the payload config is required to initialize payload.
- Error: the payload config is required for getPayload to work
- Task slug "${task.slug}" is already used by a workflow. No t
- Uploading files from URLs is disabled for collection "${coll
- beginTransaction called while no connection to the database
AI-assisted analysis of payloadcms/payload@00c58b35c0 (2026-08-12).
Data as JSON: /api/errors/7511f9cc121926f3.
Report an issue: GitHub.