payloadcms / payloadcms/payload
generate:db-schema declares the locale enum as enum__locales, but the adapter creates _locales
@r1tsuu is already working on this.
Since Aug 10, 2026.
- Dominant language
- TypeScript
- Stars
- 44.8k
- Forks
- 4.2k
- Avg merge
- 2d 21h
- Merged PRs (30d)
- 53
Description
Summary
generate:db-schema declares the locale enum with the SQL name enum__locales, but the adapter creates it in Postgres as _locales. The generated schema therefore references a type that has never existed in any database.
Two hardcoded strings that should match. A PR is attached.
Where
The adapter, which is what actually reaches the database — the SQL type name is _locales, and enum__locales is only the key in the enums map:
https://github.com/payloadcms/payload/blob/v3.85.2/packages/drizzle/src/postgres/init.ts#L22-L27
if (this.payload.config.localization) {
this.enums.enum__locales = this.pgSchema.enum(
'_locales',
this.payload.config.localization.locales.map(({ code }) => code) as [string, ...string[]],
)
}
The generator, which passes that map key where a SQL type name belongs:
if (this.payload.config.localization && enumImport) {
addEnum('enum__locales', this.payload.config.localization.localeCodes)
}
addEnum uses its name argument for both the exported const and the SQL name:
const addEnum = (name: string, options: string[]) => {
if (enumsList.some((each) => each === name)) {
return
}
enumsList.push(name)
enumDeclarations.push(
`export const ${name} = ${enumFn}('${name}', [${options.map((option) => `'${option}'`).join(', ')}])`,
)
}
That contract holds for the only other caller, which passes a real SQL type name:
addEnum(column.enumName, column.options)
So the locale call site is the single place where the two diverge.
Reproducing it
This repo already contains a reproduction — the committed fixture at
https://github.com/payloadcms/payload/blob/v3.85.2/test/relationships/payload-generated-schema.ts#L26
export const enum__locales = pgEnum('enum__locales', ['en', 'de'])
while init.ts creates that type as _locales.
In any localized Postgres project, after payload generate:db-schema:
SELECT DISTINCT udt_name FROM information_schema.columns
WHERE table_schema = 'public' AND column_name = '_locale';
-- _locales
SELECT typname FROM pg_type WHERE typname = 'enum__locales';
-- 0 rows
Impact
Every _locale column in the generated schema is typed against a type that does not exist — around 20 columns in our project. With push: false the generated file is never executed, so nothing surfaces the mismatch; it is documented as the schema mirror, so it is what people read when hand-writing migrations, and DDL written from it references a nonexistent type.
Expected
pgEnum('_locales', …), matching init.ts. The exported const should keep the name enum__locales, since columnToCodeConverter.ts#L17 emits enum__locales(…) as the column builder. Only the first argument to pgEnum is wrong.
Version
Found on 3.85.2. The cited code is byte-identical on main at the time of writing (createSchemaGenerator.ts L86/L97, init.ts L23–L25 — same lines, same content), so this is current.
Related
Two other cases where generate:db-schema output disagrees with what Payload itself creates, both concerning _status, are filed separately in payloadcms/payload#17738 — they need a design decision, whereas this one does not.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Assessment
This issue has not been assessed yet.