JuliusBrussee/caveman · error
postgres: runtime identity
Error message
postgres: runtime identity %q is not a member of %q
What it means
After confirming the identity is safe (not superuser, no BYPASSRLS, non-owner), the library checks that current_user is a member of the expected role passed to ValidateRuntimeIdentity. Membership is what authorizes SET ROLE into the tenant role; without it RLS policies keyed to that role cannot be assumed.
Solutions
- Grant membership: GRANT <expected_role> TO <login_role>; as a superuser or the role's admin.
- Verify the exact role name passed to ValidateRuntimeIdentity matches the role created in the database (check for case/whitespace).
- Confirm membership with SELECT r.rolname FROM pg_auth_members m JOIN pg_roles r ON r.oid=m.member JOIN pg_roles t ON t.oid=m.roleid WHERE t.rolname='<expected_role>';
- Ensure the same migration that creates the role also grants the membership in every environment.
Example fix
-- before -- role app_user created, expectedRole = tenant_role, no grant -- after GRANT tenant_role TO app_user;
Defensive patterns
Strategy: validation
Validate before calling
var member bool err := pool.QueryRow(ctx, ` SELECT EXISTS ( SELECT 1 FROM pg_auth_members m JOIN pg_roles cur ON cur.oid = m.member JOIN pg_roles tgt ON tgt.oid = m.roleid WHERE cur.rolname = current_user AND tgt.rolname = $1)`, role).Scan(&member)
Try / catch
if err := postgresconfig.ValidateRuntimeIdentity(ctx, pool, role); err != nil {
if strings.Contains(err.Error(), "is not a member of") {
return fmt.Errorf("run GRANT %s TO <login_role>; %w", role, err)
}
return err
} Prevention
- Include role creation AND membership grants in the same migration.
- Compare role names case-sensitively; Postgres role names are lowercase by default.
- Smoke-test role membership in CI against every target environment.
When it happens
Trigger: ValidateRuntimeIdentity (via NewRuntimePool) is called with expectedRole R, but the connected role is not (directly or transitively) a member of R per pg_auth_members.
Common situations: Typo in the expected role name; role hierarchy not provisioned (missing GRANT app_runtime TO target_role); connecting to a different database/cluster where the membership grant was never applied; staging credentials pointed at by production config.
Understand the failure class
Background: "You do not have permission" / 403 Forbidden errors: authenticated but not allowed — causes and fixes across open-source libraries — this error's family across 31 libraries.
Related errors
- postgres: runtime current_user
- postgres: unsafe runtime identity
- postgres: expected runtime role is required
- postgres: inspect runtime identity
- postgres: organization scope is required
AI-assisted analysis of JuliusBrussee/caveman@3ee70a1026 (2026-09-20).
Data as JSON: /api/errors/d449f1ca6799cfba.
Report an issue: GitHub.
Appendix: source
Thrown at shared/platform/postgresconfig/postgresconfig.go:182
`, expectedRole).Scan(¤tUser, &sessionUser, &superuser, &bypassRLS, &member, &ownsTenantTable)
if err != nil {
return fmt.Errorf("postgres: inspect runtime identity: %w", err)
}
return validateRuntimeIdentity(currentUser, sessionUser, expectedRole, superuser, bypassRLS, member, ownsTenantTable)
}
func validateRuntimeIdentity(currentUser, sessionUser, expectedRole string, superuser, bypassRLS, member, ownsTenantTable bool) error {
// A safe SET ROLE is not a safe login: session_user may execute SET ROLE
// NONE later and recover its original privileges. Runtime pools therefore
// require the authenticated identity and effective identity to be identical.
if currentUser != sessionUser {
return fmt.Errorf("postgres: runtime current_user %q differs from session_user %q", currentUser, sessionUser)
}
if superuser || bypassRLS || ownsTenantTable {
return fmt.Errorf("postgres: unsafe runtime identity %q (session_user=%q superuser=%t bypassrls=%t owns_tenant_table=%t)", currentUser, sessionUser, superuser, bypassRLS, ownsTenantTable)
}
if !member {
return fmt.Errorf("postgres: runtime identity %q is not a member of %q", currentUser, expectedRole)
}
return nil
}
type tenantTableSchema struct {
name string
nullableOrganizationID bool
rowSecurity bool
forceRowSecurity bool
policyExists bool
policyPermissive bool
policyCommand string
policyAppliesToPublic bool
policyUsingExpression string
policyCheckExpression string
}
type tenantForeignKeySchema struct {View on GitHub (pinned to 3ee70a1026)