{"record":{"id":"6af40bafa9156105","repo":"JuliusBrussee/caveman","slug":"postgres-inspect-tenant-tables-w","errorCode":null,"errorMessage":"postgres: inspect tenant tables: %w","messagePattern":"postgres: inspect tenant tables: %w","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"shared/platform/postgresconfig/postgresconfig.go","lineNumber":238,"sourceCode":"var organizationPolicyExpression = regexp.MustCompile(`^organization_id=\\(*current_setting\\('app\\.current_organization_id'(?:::text)?(?:,true)?\\)\\)*(?:::[A-Za-z_][A-Za-z0-9_.$\"]*)?$`)\n\n// ValidateTenantSchema proves structural isolation invariants that are easy to\n// regress during schema evolution. Every tenant table must fail closed under\n// FORCE RLS, and every foreign-key edge between tenant tables must carry the\n// shared organization scope (plus project scope when both tables have it).\nfunc ValidateTenantSchema(ctx context.Context, pool *pgxpool.Pool) error {\n\ttx, err := pool.BeginTx(ctx, pgx.TxOptions{\n\t\tIsoLevel:   pgx.RepeatableRead,\n\t\tAccessMode: pgx.ReadOnly,\n\t})\n\tif err != nil {\n\t\treturn fmt.Errorf(\"postgres: begin tenant schema inspection: %w\", err)\n\t}\n\tdefer tx.Rollback(ctx)\n\n\ttables, err := inspectTenantTables(ctx, tx)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"postgres: inspect tenant tables: %w\", err)\n\t}\n\tforeignKeys, err := inspectTenantForeignKeys(ctx, tx)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"postgres: inspect tenant foreign keys: %w\", err)\n\t}\n\tif violations := tenantSchemaViolations(tables, foreignKeys); len(violations) > 0 {\n\t\treturn fmt.Errorf(\"postgres: tenant schema isolation incomplete: %s\", strings.Join(violations, \"; \"))\n\t}\n\tif err := validateResolverSchema(ctx, tx); err != nil {\n\t\treturn err\n\t}\n\tif err := tx.Commit(ctx); err != nil {\n\t\treturn fmt.Errorf(\"postgres: finish tenant schema inspection: %w\", err)\n\t}\n\treturn nil\n}\n\nfunc inspectTenantTables(ctx context.Context, queryer catalogQuerier) ([]tenantTableSchema, error) {","sourceCodeStart":220,"sourceCodeEnd":256,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/3ee70a102609e550bd2e68004bf5990a9341c851/shared/platform/postgresconfig/postgresconfig.go#L220-L256","documentation":"ValidateTenantSchema opens a transaction and runs catalog queries to enumerate the tenant schema's tables. This error wraps any failure while executing inspectTenantTables against pg_catalog during that inspection. It means the introspection query itself failed (connection, permissions, or query error), not that the schema is wrong.","triggerScenarios":"Calling NewRuntimePool (which calls ValidateTenantSchema) when the catalog query in inspectTenantTables cannot be executed: the tx.Query in inspectTenantTables returns an error such as canceled context, permission denied on system catalogs, or a driver/connection failure.","commonSituations":"Database user lacks SELECT on pg_catalog/information_schema; context canceled mid-startup because the pool init is slow or shut down; transient network drop between Begin and the inspection query; misconfigured DSN pointing at a non-Postgres server that rejects the query.","solutions":["Verify the Postgres role used by the pool has permission to read catalog tables (SELECT on information_schema.tables, pg_catalog.pg_class, etc.)","Check the underlying wrapped error (%w) for context canceled / connection errors and fix connectivity or increase startup timeouts","Confirm the DSN points to the correct Postgres instance and version","Retry pool creation if the cause was a transient network failure"],"exampleFix":"// before\npool, err := NewRuntimePool(ctx, cfg) // fails: postgres: inspect tenant tables\n// after\n// grant catalog read to the app role first, then:\n// GRANT SELECT ON information_schema.tables TO app_role;\npool, err := NewRuntimePool(ctx, cfg)","handlingStrategy":"try-catch","validationCode":"if err := pool.Ping(ctx); err != nil { return fmt.Errorf(\"db unreachable before pool init: %w\", err) }","typeGuard":null,"tryCatchPattern":"if err := NewRuntimePool(ctx, cfg); err != nil {\n    var opErr *pgconn.ConnectError\n    if errors.As(err, &opErr) || errors.Is(err, context.DeadlineExceeded) {\n        // retry with backoff or fail startup with a clear message\n    }\n    return err\n}","preventionTips":["Grant the app DB role catalog read privileges as part of provisioning","Use generous startup context timeouts for pool initialization","Ping the database before creating the runtime pool"],"tags":["postgres","database","schema-inspection","query-failed"],"backgroundTag":"database-query-failed","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"}