{"record":{"id":"b3a23e0b07b0157b","repo":"vxcontrol/pentagi","slug":"failed-to-create-schema-q-w","errorCode":null,"errorMessage":"failed to create schema %q: %w","messagePattern":"failed to create schema %q: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"backend/pkg/database/tenant.go","lineNumber":60,"sourceCode":"\tdb, err := sql.Open(\"postgres\", cfg.DatabaseURL)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to open bootstrap database connection: %w\", err)\n\t}\n\tdefer db.Close()\n\n\tif err := db.PingContext(ctx); err != nil {\n\t\treturn fmt.Errorf(\"failed to reach database for tenant bootstrap: %w\", err)\n\t}\n\n\t// Serialize concurrent first boots so two instances cannot race on schema and\n\t// extension creation in the shared catalog.\n\tif err := WithAdvisoryLock(ctx, db, \"pentagi-tenant-bootstrap\", func(conn *sql.Conn) error {\n\t\t// QuoteIdentifier is belt-and-braces: ValidateTenantID already restricts\n\t\t// the character set, but this keeps the statement safe if that ever relaxes.\n\t\tif _, err := conn.ExecContext(ctx,\n\t\t\t\"CREATE SCHEMA IF NOT EXISTS \"+pq.QuoteIdentifier(schema),\n\t\t); err != nil {\n\t\t\treturn fmt.Errorf(\"failed to create schema %q: %w\", schema, err)\n\t\t}\n\n\t\tfor _, ext := range requiredExtensions {\n\t\t\tif err := ensureSharedExtension(ctx, conn, ext, extSchema); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\n\t\treturn nil\n\t}); err != nil {\n\t\treturn err\n\t}\n\n\t// Rewrite the DSN once; every consumer reads cfg.DatabaseURL afterwards.\n\treturn RewriteDatabaseURLForTenant(cfg)\n}\n\n// RewriteDatabaseURLForTenant appends the tenant search_path to cfg.DatabaseURL.","sourceCodeStart":42,"sourceCodeEnd":78,"githubUrl":"https://github.com/vxcontrol/pentagi/blob/ea665308baaff015b226f308438a68d929d0f29b/backend/pkg/database/tenant.go#L42-L78","documentation":"Inside the tenant-bootstrap advisory lock, the code runs CREATE SCHEMA IF NOT EXISTS <schema> (name quoted with pq.QuoteIdentifier). This error wraps any PostgreSQL rejection of that DDL — the schema name itself is pre-validated, so failures are almost always privilege or server-state problems. Bootstrap aborts and the tenant's search_path is never wired up.","triggerScenarios":"The connected role lacks CREATE privilege on the database, the connection was dropped mid-bootstrap (context canceled, server restart), or a syntax/serialization issue occurs during CREATE SCHEMA IF NOT EXISTS for the tenant schema.","commonSituations":"Deploying with a least-privilege DB user that can connect but cannot create schemas; running bootstrap while the DB is in read-only mode; connection pooler (pgbouncer) dropping the session mid-statement; context timeout expiring before DDL completes.","solutions":["Grant the role CREATE on the database once: GRANT CREATE ON DATABASE pentagi TO appuser; or pre-create the tenant schema as an admin.","Confirm the DB is not in read-only mode (SHOW transaction_read_only;).","Retry the boot — CREATE SCHEMA IF NOT EXISTS is idempotent and the advisory lock serializes concurrent runs.","Check the wrapped pg error for the exact server reason (permission denied vs duplicate vs admin shutdown)."],"exampleFix":"// before (as appuser without DDL rights)\nCREATE SCHEMA IF NOT EXISTS tenant_acme; -- ERROR: permission denied\n// after (run once as superuser)\nGRANT CREATE ON DATABASE pentagi TO appuser; -- or: CREATE SCHEMA tenant_acme AUTHORIZATION appuser;","handlingStrategy":"validation","validationCode":"// preflight as the app role, before starting the app\npsql \"$DATABASE_URL\" -c \"CREATE SCHEMA IF NOT EXISTS tenant_check;\" \\\n  && psql \"$DATABASE_URL\" -c \"DROP SCHEMA tenant_check;\"","typeGuard":null,"tryCatchPattern":"if err := EnsureTenantSchema(ctx, cfg); err != nil {\n    if strings.Contains(err.Error(), \"permission denied\") {\n        log.Error().Msg(\"grant CREATE ON DATABASE to the app role or pre-create the tenant schema\")\n    }\n    return err\n}","preventionTips":["Provision the app role with CREATE ON DATABASE, or pre-create tenant schemas via IaC","Keep the DB out of read-only mode during deploys","Connect bootstrap directly to Postgres, not through pgbouncer","Re-run boot after fixing: CREATE SCHEMA IF NOT EXISTS is idempotent"],"tags":["postgres","schema","permissions","multi-tenancy"],"backgroundTag":"postgres-permission-denied","analyzedSha":"ea665308baaff015b226f308438a68d929d0f29b","analyzedAt":"2026-09-01T14:16:31.421Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T15:18:49.778Z"}