MHSanaei/3x-ui · error

destination DSN is required

Error message

destination DSN is required

What it means

MigrateData (SQLite→Postgres migration) requires a non-empty destination DSN and rejects the call before touching either database when dstDSN is ''. It is the symmetric guard to the XUI_DB_TYPE check: copying rows into 'no database' is meaningless. The source file at srcPath must also exist (checked immediately before).

Source

Thrown at internal/database/migrate_data.go:72

		&model.Host{},
		&model.NodeClientTraffic{},
		&model.NodeClientIp{},
		&model.ClientGlobalTraffic{},
		&model.OutboundSubscription{},
	}
}

// MigrateData copies every row from the configured SQLite file at srcPath into
// a fresh PostgreSQL database described by dstDSN. The destination tables are
// (re)created with AutoMigrate; truncate and copy then run in one transaction,
// so a failed migration leaves the destination data unchanged. Source data is
// left untouched.
func MigrateData(srcPath, dstDSN string) error {
	if _, err := os.Stat(srcPath); err != nil {
		return fmt.Errorf("source sqlite not found at %s: %w", srcPath, err)
	}
	if dstDSN == "" {
		return errors.New("destination DSN is required")
	}

	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})

View on GitHub (pinned to ad32144c42)

Solutions

  1. Pass a valid Postgres DSN: ./x-ui migrate-db --dst 'postgres://user:pass@host:5432/xui?sslmode=disable' (or set the env var your wrapper reads)
  2. Confirm the source path exists at the reported location before retrying
  3. Echo the variable before running if a script builds the flag dynamically

Example fix

# before
./x-ui migrate --src /etc/x-ui/x-ui.db --dst ""

# after
./x-ui migrate --src /etc/x-ui/x-ui.db --dst "postgres://xui:pass@10.0.0.5:5432/xui"
Defensive patterns

Strategy: validation

Validate before calling

if dstDSN == "" {
    return errors.New("pass the postgres DSN, e.g. postgres://user:pass@host:5432/xui")
}

Try / catch

if err := database.MigrateData(src, dst); err != nil {
    if strings.Contains(err.Error(), "destination DSN is required") {
        // fix invocation, not data: prompt for DSN and re-run
    }
    return err
}

Prevention

When it happens

Trigger: Running the migration CLI/endpoint with the --dst flag omitted or an empty string; a wrapper script that reads the DSN from an unset env var and passes '' through.

Common situations: First-time migration to Postgres where the operator forgets to export the DSN variable the script expects; shell quoting mistakes that expand to empty.

Related errors


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