{"id":"040118a4e6896f0a","repo":"drizzle-team/drizzle-orm","slug":"crudpolicy-requires-a-read-policy","errorCode":null,"errorMessage":"crudPolicy requires a read policy","messagePattern":"crudPolicy requires a read policy","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"drizzle-orm/src/neon/rls.ts","lineNumber":21,"sourceCode":"import { PgRole, pgRole } from '~/pg-core/roles.ts';\nimport { type SQL, sql } from '~/sql/sql.ts';\n\n/**\n * Generates a set of PostgreSQL row-level security (RLS) policies for CRUD operations based on the provided options.\n *\n * @param options - An object containing the policy configuration.\n * @param options.role - The PostgreSQL role(s) to apply the policy to. Can be a single `PgRole` instance or an array of `PgRole` instances or role names.\n * @param options.read - The SQL expression or boolean value that defines the read policy. Set to `true` to allow all reads, `false` to deny all reads, or provide a custom SQL expression. Set to `null` to prevent the policy from being generated.\n * @param options.modify - The SQL expression or boolean value that defines the modify (insert, update, delete) policies. Set to `true` to allow all modifications, `false` to deny all modifications, or provide a custom SQL expression. Set to `null` to prevent policies from being generated.\n * @returns An array of PostgreSQL policy definitions, one for each CRUD operation.\n */\nexport const crudPolicy = (options: {\n\trole: PgPolicyToOption;\n\tread: SQL | boolean | null;\n\tmodify: SQL | boolean | null;\n}) => {\n\tif (options.read === undefined) {\n\t\tthrow new Error('crudPolicy requires a read policy');\n\t}\n\n\tif (options.modify === undefined) {\n\t\tthrow new Error('crudPolicy requires a modify policy');\n\t}\n\n\tlet read: SQL | undefined;\n\tif (options.read === true) {\n\t\tread = sql`true`;\n\t} else if (options.read === false) {\n\t\tread = sql`false`;\n\t} else if (options.read !== null) {\n\t\tread = options.read;\n\t}\n\n\tlet modify: SQL | undefined;\n\tif (options.modify === true) {\n\t\tmodify = sql`true`;","sourceCodeStart":3,"sourceCodeEnd":39,"githubUrl":"https://github.com/drizzle-team/drizzle-orm/blob/b7862528fd8fc39bc2653a6c18dad7c1f4e68d10/drizzle-orm/src/neon/rls.ts#L3-L39","documentation":"An Error 'crudPolicy requires a read policy' thrown by crudPolicy() in drizzle-orm/src/neon/rls.ts:21 when options.read === undefined. crudPolicy generates the four RLS policies (select/insert/update/delete) for a role; it requires an explicit read decision (true, false, null, or a SQL expression) because omitting it is almost certainly a mistake. Note null is valid (suppresses the select policy) — only undefined throws.","triggerScenarios":"Calling crudPolicy({ role, modify: ... }) and forgetting the read field. TypeScript's declared type (read: SQL | boolean | null) does not include undefined, but undefined slips through when the object is built dynamically or cast through any.","commonSituations":"Building policy config from partial config objects, spreading defaults, or passing an any-typed payload where the read key is absent. The runtime guard catches what the type system should have prevented.","solutions":["Provide an explicit read value: true (allow all), false (deny all), null (no select policy), or a SQL expression.","If constructing options dynamically, default read explicitly instead of leaving it unset.","Tighten the config object's type so TS flags the missing field at compile time."],"exampleFix":"// before\ncrudPolicy({ role: authenticatedRole, modify: sql`(select auth.user_id() = ${posts.userId})` });\n// throws: crudPolicy requires a read policy\n\n// after\ncrudPolicy({\n  role: authenticatedRole,\n  read: sql`(select auth.user_id() = ${posts.userId})`,\n  modify: sql`(select auth.user_id() = ${posts.userId})`,\n});","handlingStrategy":"validation","validationCode":"import type { crudPolicy } from 'drizzle-orm/neon';\n\ntype CrudOpts = Parameters<typeof crudPolicy>[0];\n\nfunction assertCrudPolicyOptions(o: CrudOpts): void {\n  if (o.read === undefined) {\n    throw new Error('crudPolicy: set `read` (true | false | null | SQL). null suppresses the select policy.');\n  }\n}\n\nassertCrudPolicyOptions(opts);","typeGuard":"function hasReadPolicy(o: { read?: unknown }): boolean {\n  return o.read !== undefined;\n}","tryCatchPattern":null,"preventionTips":["Always pass read explicitly — use null when you want no select policy.","Build crudPolicy options from a fully-typed helper, not from partial/any objects.","Enable strict TS so undefined is not silently allowed."],"tags":["postgres","neon","rls","policy","crud-policy","validation"],"analyzedSha":"b7862528fd8fc39bc2653a6c18dad7c1f4e68d10","analyzedAt":"2026-08-03T18:11:14.318Z","schemaVersion":2}