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

  1. Test the exact DSN out-of-band: psql '<DSN>' -c 'select 1' from the same host/container.
  2. Pre-create the target database if the server disallows creation on connect, and URL-encode special characters in the password.
  3. 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

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


AI-assisted analysis of MHSanaei/3x-ui@ad32144c42 (2026-08-15). Data as JSON: /api/errors/d230b466c922605e. Report an issue: GitHub.