{"record":{"id":"a6a95b85fa011972","repo":"vxcontrol/pentagi","slug":"failed-to-resolve-current-schema-w","errorCode":null,"errorMessage":"failed to resolve current schema: %w","messagePattern":"failed to resolve current schema: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"backend/pkg/database/tenant.go","lineNumber":171,"sourceCode":"\t\treturn \"\", nil\n\tcase err != nil:\n\t\treturn \"\", fmt.Errorf(\"failed to resolve schema of extension %q: %w\", ext, err)\n\tdefault:\n\t\treturn schema, nil\n\t}\n}\n\n// VerifySearchPath asserts that connections really do resolve into the expected\n// schema. A typo in the DSN would otherwise route a tenant silently onto public,\n// where every tenant would share one dataset — a quiet, catastrophic failure.\nfunc VerifySearchPath(ctx context.Context, db *sql.DB, cfg *config.Config) error {\n\tif !cfg.HasTenant() {\n\t\treturn nil\n\t}\n\n\tvar current sql.NullString\n\tif err := db.QueryRowContext(ctx, \"SELECT current_schema()\").Scan(&current); err != nil {\n\t\treturn fmt.Errorf(\"failed to resolve current schema: %w\", err)\n\t}\n\tif current.String != cfg.SchemaName() {\n\t\treturn fmt.Errorf(\n\t\t\t\"search_path resolved to schema %q, expected %q — refusing to start so tenants \"+\n\t\t\t\t\"do not silently share one dataset\",\n\t\t\tcurrent.String, cfg.SchemaName(),\n\t\t)\n\t}\n\n\treturn nil\n}\n\n// RunMigrations applies pending migrations while holding an advisory lock, so\n// that two instances booting simultaneously cannot execute the same migration\n// set concurrently. Without a tenant the lock is still taken, which also fixes\n// the pre-existing race between two single-instance deployments sharing a\n// database.\nfunc RunMigrations(ctx context.Context, db *sql.DB, cfg *config.Config, up func(*sql.DB) error) error {","sourceCodeStart":153,"sourceCodeEnd":189,"githubUrl":"https://github.com/vxcontrol/pentagi/blob/ea665308baaff015b226f308438a68d929d0f29b/backend/pkg/database/tenant.go#L153-L189","documentation":"VerifySearchPath runs SELECT current_schema() to confirm the connection actually resolves into the tenant's schema after the DSN search_path rewrite. This error wraps a failure of that probe query itself — the connection is unusable (network, auth, canceled context), not misrouted. It is called from main and the installer's password-reset path.","triggerScenarios":"After RewriteDatabaseURLForTenant appends search_path to DATABASE_URL, a pool/ping query SELECT current_schema() fails: DSN became malformed after the rewrite, pooler rejects the options/search_path parameter, DB unreachable, or ctx canceled.","commonSituations":"DATABASE_SEARCH_PATH_VIA_OPTIONS mismatch with pgbouncer/Supavisor which may strip or reject options; a libpq keyword DSN that the rewritten string broke; connection dropped between open and probe; wrong TLS settings on the rewritten URL.","solutions":["Test the exact rewritten DSN with psql \"<DATABASE_URL>\" -c 'SELECT current_schema();' to see the raw driver error.","If behind pgbouncer, set DATABASE_SEARCH_PATH_VIA_OPTIONS=true so search_path is sent as options=--search_path=... .","Restore connectivity/credentials on the rewritten URL; verify the URL-style DSN still parses (special chars in password percent-encoded).","Retry — transient network failures resolve on restart."],"exampleFix":"// before\nDATABASE_URL=postgres://u:p@pgbouncer:6543/db?search_path=tenant_acme  # pooler ignores search_path\n// after\n# .env\nDATABASE_SEARCH_PATH_VIA_OPTIONS=true  # sends options=--search_path=tenant_acme,public","handlingStrategy":"validation","validationCode":"// validate the rewritten DSN resolves to the tenant schema before app start\npsql \"$DATABASE_URL\" -c \"SELECT current_schema();\"  # must return the tenant schema","typeGuard":null,"tryCatchPattern":"if err := VerifySearchPath(ctx, db, cfg); err != nil {\n    if strings.Contains(err.Error(), \"failed to resolve current schema\") {\n        log.Error().Err(err).Msg(\"DSN unusable after search_path rewrite; check pooler options and TLS\")\n    }\n    return err\n}","preventionTips":["Test the final DATABASE_URL with psql after any change to search_path settings","Set DATABASE_SEARCH_PATH_VIA_OPTIONS=true when behind pgbouncer/Supavisor","Percent-encode the DB password so URL rewriting cannot corrupt the DSN","Keep a direct-to-Postgres path for health probes"],"tags":["postgres","search-path","connectivity","multi-tenancy"],"backgroundTag":"db-probe-query-failed","analyzedSha":"ea665308baaff015b226f308438a68d929d0f29b","analyzedAt":"2026-09-01T14:16:31.421Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}