{"record":{"id":"d230b466c922605e","repo":"MHSanaei/3x-ui","slug":"open-postgres-destination-w","errorCode":null,"errorMessage":"open postgres destination: %w","messagePattern":"open postgres destination: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/database/migrate_data.go","lineNumber":92,"sourceCode":"\n\tif err := os.MkdirAll(path.Dir(srcPath), 0o755); err != nil {\n\t\treturn err\n\t}\n\n\tsrcDSN := srcPath + \"?_journal_mode=WAL&_busy_timeout=10000\"\n\tsrc, err := gorm.Open(sqlite.Open(srcDSN), &gorm.Config{Logger: logger.Discard})\n\tif err != nil {\n\t\treturn fmt.Errorf(\"open sqlite source: %w\", err)\n\t}\n\tsrcSQL, err := src.DB()\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer srcSQL.Close()\n\n\tdst, err := gorm.Open(postgres.Open(dstDSN), &gorm.Config{Logger: logger.Discard})\n\tif err != nil {\n\t\treturn fmt.Errorf(\"open postgres destination: %w\", err)\n\t}\n\tdstSQL, err := dst.DB()\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer dstSQL.Close()\n\tdstSQL.SetConnMaxLifetime(time.Hour)\n\n\tlog.Println(\"Creating destination schema...\")\n\tfor _, m := range migrationModels() {\n\t\tif err := dst.AutoMigrate(m); err != nil {\n\t\t\treturn fmt.Errorf(\"AutoMigrate %T: %w\", m, err)\n\t\t}\n\t}\n\n\ttotalRows := 0\n\ttxErr := dst.Transaction(func(tx *gorm.DB) error {\n\t\t// AutoMigrate re-creates the legacy client_traffics -> inbounds foreign key,","sourceCodeStart":74,"sourceCodeEnd":110,"githubUrl":"https://github.com/MHSanaei/3x-ui/blob/ad32144c42455696ea9f14e12168beac3e25f5d2/internal/database/migrate_data.go#L74-L110","documentation":"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).","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"# before\nXUI_DB_DSN='postgres://user:p@ss@host:5432/xui' # p@ss breaks parsing\n# after\nXUI_DB_DSN='postgres://user:p%40ss@host:5432/xui'","handlingStrategy":"validation","validationCode":"sqlDB, err := sql.Open(\"pgx\", dstDSN)\nif err != nil { return err }\ndefer sqlDB.Close()\nctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)\ndefer cancel()\nif err := sqlDB.PingContext(ctx); err != nil {\n    return fmt.Errorf(\"destination DSN unusable: %w\", err)\n}","typeGuard":null,"tryCatchPattern":"if err := database.MigrateData(src, dsn); err != nil {\n    if strings.Contains(err.Error(), \"open postgres destination\") {\n        return fixDSNHint(err) // surface auth/sslmode/host hints\n    }\n    return err\n}","preventionTips":["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."],"tags":["database","postgres","connection","migration"],"backgroundTag":null,"analyzedSha":"ad32144c42455696ea9f14e12168beac3e25f5d2","analyzedAt":"2026-08-15T11:13:23.905Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}