MHSanaei/3x-ui · error
restore failed: %w
Error message
restore failed: %w
What it means
RestoreSQLite executed the dump's SQL via one multi-statement ExecContext and the driver returned an error — the dump did not apply. The function closes the connection and removes the half-built dstPath, so no partial database survives. Root causes live in the wrapped error: malformed/truncated dump, SQL features the SQLite build rejects, or destination filesystem problems.
Source
Thrown at internal/database/dump_sqlite.go:134
}
if _, err := os.Stat(dstPath); err == nil {
return fmt.Errorf("destination already exists: %s", dstPath)
}
gdb, err := gorm.Open(sqlite.Open(dstPath), &gorm.Config{Logger: logger.Discard})
if err != nil {
return err
}
sqlDB, err := gdb.DB()
if err != nil {
return err
}
// mattn/go-sqlite3 executes every statement in a multi-statement string.
if _, err := sqlDB.ExecContext(context.Background(), string(script)); err != nil {
sqlDB.Close()
os.Remove(dstPath)
return fmt.Errorf("restore failed: %w", err)
}
return sqlDB.Close()
}
// dumpTableData appends one INSERT statement per row of table to b.
func dumpTableData(db *sql.DB, table string, b *strings.Builder) error {
rows, err := db.QueryContext(context.Background(), `SELECT * FROM "`+table+`"`)
if err != nil {
return err
}
defer rows.Close()
cols, err := rows.Columns()
if err != nil {
return err
}
n := len(cols)
prefix := `INSERT INTO "` + table + `" VALUES(`View on GitHub (pinned to ad32144c42)
Solutions
- Read the wrapped driver error — 'near ... syntax error' means dump corruption, 'disk I/O error'/'database or disk is full' means storage.
- Re-generate the dump and transfer it in binary-safe mode (scp, not clipboard); compare sizes/checksums.
- If the dump came from `sqlite3 .dump` of a much newer SQLite, re-dump with plain features or update the panel so its bundled SQLite matches.
Defensive patterns
Strategy: try-catch
Validate before calling
// Sanity-check the dump before restoring:
head, err := os.ReadFile(dumpPath)
if err != nil { return err }
if !bytes.HasPrefix(head, []byte("-- SQLite dump")) && !bytes.Contains(head, []byte("BEGIN TRANSACTION;")) {
return errors.New("file does not look like a sqlite dump")
} Try / catch
if err := database.RestoreSQLite(dumpPath, dstPath); err != nil {
if strings.Contains(err.Error(), "restore failed") {
// dstPath was removed by the function; fix the dump then retry
return diagnoseDump(dumpPath)
}
return err
} Prevention
- Transfer dumps binary-safe (scp/rsync) and compare checksums before restoring.
- Prefer dumps produced by DumpSQLite so SQLite versions match.
- Restore into a scratch path first, integrity_check it, then swap in.
When it happens
Trigger: Dump file truncated during transfer (checksum mismatch not caught earlier); hand-edited dump with a syntax error; dump produced by a newer SQLite with features the bundled CGo SQLite lacks; disk full while replaying INSERTs.
Common situations: Moving dumps between hosts via copy/paste or proxies that mangle bytes; restoring dumps edited in tools that re-encode quotes/encoding; containers with a full overlay FS.
Related errors
- sqlite integrity check failed: %s
- destination already exists: %s
- open sqlite source: %w
- sqlite backup is unavailable for PostgreSQL
- database is not initialized
AI-assisted analysis of MHSanaei/3x-ui@ad32144c42 (2026-08-15).
Data as JSON: /api/errors/6b27a6b8100958a9.
Report an issue: GitHub.