JuliusBrussee/caveman · error
postgres: runtime current_user
Error message
postgres: runtime current_user %q differs from session_user %q
What it means
For a runtime pool, the authenticated identity (session_user) and the effective identity (current_user) must be identical. If they differ, something executed SET ROLE, which could later be undone with SET ROLE NONE to regain elevated privileges — so the library fails the validation.
Solutions
- Connect directly as the intended low-privilege role: put that role in the database URL/user field and remove any `options=-c%20role=...` from the connection string.
- Remove any server-side login triggers or role settings that execute SET ROLE on connection.
- If using PgBouncer, ensure the frontend and backend usernames match (no user emulation) or bypass the proxy for runtime pools.
- Verify with `SELECT current_user, session_user;` using the same connection parameters.
Example fix
// before url := "postgres://admin:pw@db/app?options=-c%20role=tenant_role" // after url := "postgres://tenant_role:pw@db/app" // direct login as the role
Defensive patterns
Strategy: validation
Validate before calling
var cu, su string err := pool.QueryRow(ctx, "SELECT current_user, session_user").Scan(&cu, &su) // require cu == su before building runtime pools
Try / catch
if err := postgresconfig.ValidateRuntimeIdentity(ctx, pool, role); err != nil {
if strings.Contains(err.Error(), "differs from session_user") {
log.Fatal("remove SET ROLE/options from the runtime connection string")
}
return err
} Prevention
- Never put `options=-c role=...` in runtime pool connection strings.
- Avoid login triggers or ALTER ROLE settings that switch roles on connect.
- Do not front runtime pools with pgbouncer configs that substitute users.
When it happens
Trigger: ValidateRuntimeIdentity (via NewRuntimePool) connects as role A but current_user reports role B — typically because the connection string includes `options=-c role=...`, a search_path/SET ROLE hook, a pooled proxy (pgbouncer) that re-authenticates differently, or a login trigger issuing SET ROLE.
Common situations: Reusing a pool URL that was hardened for an admin role with SET ROLE semantics; connecting through PgBouncer configured with a different user; database-side `ALTER ROLE ... SET role` behavior or startup scripts switching roles.
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: inspect runtime identity
- postgres: runtime identity
- postgres: unsafe runtime identity
- postgres: expected runtime role is required
- postgres: organization scope is required
AI-assisted analysis of JuliusBrussee/caveman@3ee70a1026 (2026-09-20).
Data as JSON: /api/errors/184d93f14fe6dac3.
Report an issue: GitHub.
Appendix: source
Thrown at shared/platform/postgresconfig/postgresconfig.go:176
WHERE n.nspname='public'
AND c.relkind IN ('r','p')
AND pg_get_userbyid(c.relowner)=current_user
)
FROM pg_roles r
WHERE r.rolname=current_user
`, 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 stringView on GitHub (pinned to 3ee70a1026)