{"record":{"id":"9838c9e72abcf245","repo":"JuliusBrussee/caveman","slug":"postgres-expected-runtime-role-is-required","errorCode":null,"errorMessage":"postgres: expected runtime role is required","messagePattern":"postgres: expected runtime role is required","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"shared/platform/postgresconfig/postgresconfig.go","lineNumber":140,"sourceCode":"\t\treturn nil, err\n\t}\n\tif err := ValidateRuntimeIdentity(ctx, pool, expectedRole); err != nil {\n\t\tpool.Close()\n\t\treturn nil, err\n\t}\n\tif err := ValidateTenantSchema(ctx, pool); err != nil {\n\t\tpool.Close()\n\t\treturn nil, err\n\t}\n\treturn pool, nil\n}\n\n// ValidateRuntimeIdentity rejects database identities that can bypass RLS or\n// do not inherit the service's audited privilege group.\nfunc ValidateRuntimeIdentity(ctx context.Context, pool *pgxpool.Pool, expectedRole string) error {\n\texpectedRole = strings.TrimSpace(expectedRole)\n\tif expectedRole == \"\" {\n\t\treturn errors.New(\"postgres: expected runtime role is required\")\n\t}\n\tvar currentUser, sessionUser string\n\tvar superuser, bypassRLS, member, ownsTenantTable bool\n\terr := pool.QueryRow(ctx, `\n\t\tSELECT current_user,\n\t\t       session_user,\n\t\t       r.rolsuper,\n\t\t       r.rolbypassrls,\n\t\t       pg_has_role(current_user, $1, 'MEMBER'),\n\t\t       EXISTS (\n\t\t         SELECT 1\n\t\t         FROM pg_class c\n\t\t         JOIN pg_namespace n ON n.oid=c.relnamespace\n\t\t         JOIN information_schema.columns col\n\t\t           ON col.table_schema=n.nspname\n\t\t          AND col.table_name=c.relname\n\t\t          AND col.column_name='organization_id'\n\t\t         WHERE n.nspname='public'","sourceCodeStart":122,"sourceCodeEnd":158,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/3ee70a102609e550bd2e68004bf5990a9341c851/shared/platform/postgresconfig/postgresconfig.go#L122-L158","documentation":"ValidateRuntimeIdentity checks that the pool's database user cannot bypass RLS and belongs to the expected audited role group; it refuses to run that check at all when expectedRole is blank. An empty expected role would make the membership assertion vacuous, so the library fails fast before issuing any query.","triggerScenarios":"Calling ValidateRuntimeIdentity(ctx, pool, \"\") or with a whitespace-only string (spaces/tabs are trimmed to empty); passing a config value that was never populated, e.g. an unset ROLE env var defaulted to \"\".","commonSituations":"New services wired to NewRuntimePool before the runtime-role config key was added; template manifests rendering an empty role; tests forgetting to pass the role constant.","solutions":["Pass the intended database role name, e.g. ValidateRuntimeIdentity(ctx, pool, \"svc_orders_runtime\")","Populate the role from configuration and fail deployment earlier if it is missing","Trim and check the role string at config-load time so this reaches the caller as a config error"],"exampleFix":"// before\nif err := ValidateRuntimeIdentity(ctx, pool, os.Getenv(\"PG_ROLE\")); err != nil {...} // PG_ROLE unset\n// after\nrole := os.Getenv(\"PG_ROLE\")\nif strings.TrimSpace(role) == \"\" { return fmt.Errorf(\"PG_ROLE must be set\") }\nif err := ValidateRuntimeIdentity(ctx, pool, role); err != nil {...}","handlingStrategy":"validation","validationCode":"role := strings.TrimSpace(expectedRole)\nif role == \"\" {\n    return fmt.Errorf(\"expected runtime role must be configured before ValidateRuntimeIdentity\")\n}","typeGuard":null,"tryCatchPattern":"if err := postgresconfig.ValidateRuntimeIdentity(ctx, pool, role); err != nil {\n    if strings.Contains(err.Error(), \"expected runtime role is required\") {\n        return fmt.Errorf(\"runtime role not configured: set PG_ROLE before starting\")\n    }\n    return err\n}","preventionTips":["Fail at config load if the runtime-role key is missing or blank","Never default the role to the empty string; use a sentinel that fails loudly","Trim whitespace from role values loaded from env/secrets","Add a startup assertion that ValidateRuntimeIdentity runs with a concrete role in every environment"],"tags":["postgres","rls","validation","configuration"],"backgroundTag":"empty-required-field","analyzedSha":"3ee70a102609e550bd2e68004bf5990a9341c851","analyzedAt":"2026-09-20T15:53:39.229Z","contentChangedAt":"2026-09-20T15:53:39.229Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}