MHSanaei/3x-ui · error
open postgres destination: %w
Error message
open postgres destination: %w
What it means
MigrateData opens the PostgreSQL destination with the provided DSN and wraps the failure. Unlike the panel's InitDB, this path has no retry loop — a single failed open aborts the migration. The wrapped gorm/libpq error carries the real reason (DNS, refused, auth, TLS, unknown database).
Source
Thrown at internal/database/migrate_data.go:92
if err := os.MkdirAll(path.Dir(srcPath), 0o755); err != nil {
return err
}
srcDSN := srcPath + "?_journal_mode=WAL&_busy_timeout=10000"
src, err := gorm.Open(sqlite.Open(srcDSN), &gorm.Config{Logger: logger.Discard})
if err != nil {
return fmt.Errorf("open sqlite source: %w", err)
}
srcSQL, err := src.DB()
if err != nil {
return err
}
defer srcSQL.Close()
dst, err := gorm.Open(postgres.Open(dstDSN), &gorm.Config{Logger: logger.Discard})
if err != nil {
return fmt.Errorf("open postgres destination: %w", err)
}
dstSQL, err := dst.DB()
if err != nil {
return err
}
defer dstSQL.Close()
dstSQL.SetConnMaxLifetime(time.Hour)
log.Println("Creating destination schema...")
for _, m := range migrationModels() {
if err := dst.AutoMigrate(m); err != nil {
return fmt.Errorf("AutoMigrate %T: %w", m, err)
}
}
totalRows := 0
txErr := dst.Transaction(func(tx *gorm.DB) error {
// AutoMigrate re-creates the legacy client_traffics -> inbounds foreign key,View on GitHub (pinned to ad32144c42)
Solutions
- Test the exact DSN out-of-band: psql '<DSN>' -c 'select 1' from the same host/container.
- Pre-create the target database if the server disallows creation on connect, and URL-encode special characters in the password.
- Ensure postgres is up (healthcheck/depends_on) before running migrate-db.
Example fix
# before XUI_DB_DSN='postgres://user:p@ss@host:5432/xui' # p@ss breaks parsing # after XUI_DB_DSN='postgres://user:p%40ss@host:5432/xui'
Defensive patterns
Strategy: validation
Validate before calling
sqlDB, err := sql.Open("pgx", dstDSN)
if err != nil { return err }
defer sqlDB.Close()
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
if err := sqlDB.PingContext(ctx); err != nil {
return fmt.Errorf("destination DSN unusable: %w", err)
} Try / catch
if err := database.MigrateData(src, dsn); err != nil {
if strings.Contains(err.Error(), "open postgres destination") {
return fixDSNHint(err) // surface auth/sslmode/host hints
}
return err
} Prevention
- psql-test the exact DSN from the same environment before migrating.
- URL-encode special characters in passwords; use sslmode matching server policy.
- Ensure the target database exists and postgres is healthy before the one-shot migrate.
When it happens
Trigger: dstDSN with a wrong password or username; postgres not yet reachable (container race); database named in the DSN does not exist; sslmode mismatch with server policy.
Common situations: One-shot migrations run before postgres is healthy; DSN hand-assembled with unescaped special characters in the password; managed PG (RDS/Supabase) requiring sslmode=require.
Related errors
- destination DSN is required
- postgres unreachable after %d attempts: %w
- source sqlite not found at %s: %w
- AutoMigrate %T: %w
- drop legacy foreign key: %w
AI-assisted analysis of MHSanaei/3x-ui@ad32144c42 (2026-08-15).
Data as JSON: /api/errors/d230b466c922605e.
Report an issue: GitHub.