MHSanaei/3x-ui · warning

sqlite backup is unavailable for PostgreSQL

Error message

sqlite backup is unavailable for PostgreSQL

What it means

BackupSQLite guards at the top with IsPostgres() and refuses to run when the panel is booted against PostgreSQL, because there is no SQLite file to back up — the logical database lives in Postgres. It is a deliberate fail-fast guard, not a corruption signal. Callers that offer a 'backup database' button should hide or re-route it when Postgres is active.

Source

Thrown at internal/database/db.go:2182

}

func IsNotFound(err error) bool {
	return errors.Is(err, gorm.ErrRecordNotFound)
}

func IsSQLiteDB(file io.ReaderAt) (bool, error) {
	signature := []byte("SQLite format 3\x00")
	buf := make([]byte, len(signature))
	_, err := file.ReadAt(buf, 0)
	if err != nil {
		return false, err
	}
	return bytes.Equal(buf, signature), nil
}

func BackupSQLite(dstPath string) (err error) {
	if IsPostgres() {
		return errors.New("sqlite backup is unavailable for PostgreSQL")
	}
	if db == nil {
		return errors.New("database is not initialized")
	}
	if _, err := os.Lstat(dstPath); err == nil {
		return fmt.Errorf("sqlite backup destination already exists: %s", dstPath)
	} else if !errors.Is(err, os.ErrNotExist) {
		return err
	}
	defer func() {
		if err != nil {
			_ = os.Remove(dstPath)
		}
	}()

	ctx, cancel := context.WithTimeout(context.Background(), backupSQLiteTimeout)
	defer cancel()

View on GitHub (pinned to ad32144c42)

Solutions

  1. Use a Postgres-native backup instead: pg_dump against the DSN in XUI_DB_DSN
  2. If code calls BackupSQLite, branch on database.IsPostgres() first and export via ExportPostgresToSQLite if a .db file is genuinely wanted
  3. Remove or condition scheduled backup jobs that assume SQLite

Example fix

// before
err := database.BackackSQLite(dst) // typo aside, called unconditionally

// after
if database.IsPostgres() {
    return fmt.Errorf("use pg_dump; sqlite backup unavailable under postgres")
}
err := database.BackupSQLite(dst)
Defensive patterns

Strategy: validation

Validate before calling

if database.IsPostgres() {
    return errors.New("skip sqlite backup: panel runs postgres; use pg_dump")
}
return database.BackupSQLite(dst)

Try / catch

if err := database.BackupSQLite(dst); err != nil {
    if strings.Contains(err.Error(), "unavailable for PostgreSQL") {
        // route to pg_dump instead of failing the backup job
    }
}

Prevention

When it happens

Trigger: Invoking the backup endpoint (panel settings backup or CLI path that calls database.BackupSQLite) while XUI_DB_TYPE=postgres; a cron or script written against SQLite that survived a migration to Postgres.

Common situations: After migrating SQLite→Postgres the admin still clicks the old backup button; automation scripts calling the backup API unchanged post-migration.

Related errors


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