MHSanaei/3x-ui · error
open sqlite destination: %w
Error message
open sqlite destination: %w
What it means
Thrown by the PostgreSQL-to-SQLite export when gorm.Open(sqlite.Open(dstPath+"?_busy_timeout=10000")) fails on the destination file. The code already removed any existing file at dstPath and created its parent directory, so an open failure means the filesystem itself refused: unreadable directory, read-only mount, disk full, or a locked path (e.g. pointing at a directory).
Source
Thrown at internal/database/migrate_data.go:181
// Start from an empty file so AutoMigrate creates the canonical schema.
if err := os.Remove(dstPath); err != nil && !os.IsNotExist(err) {
return err
}
src, err := gorm.Open(postgres.Open(srcDSN), &gorm.Config{Logger: logger.Discard})
if err != nil {
return fmt.Errorf("open postgres source: %w", err)
}
srcSQL, err := src.DB()
if err != nil {
return err
}
defer srcSQL.Close()
// No WAL: keep all data in the main file so it is complete once closed.
dst, err := gorm.Open(sqlite.Open(dstPath+"?_busy_timeout=10000"), &gorm.Config{Logger: logger.Discard})
if err != nil {
return fmt.Errorf("open sqlite destination: %w", err)
}
dstSQL, err := dst.DB()
if err != nil {
return err
}
defer dstSQL.Close()
return copyAllModels(src, dst)
}
// copyAllModels (re)creates the schema on dst and copies every migrated table
// from src to dst in FK-safe order. src/dst may be any gorm backend.
func copyAllModels(src, dst *gorm.DB) error {
for _, m := range migrationModels() {
if err := dst.AutoMigrate(m); err != nil {
return fmt.Errorf("AutoMigrate %T: %w", m, err)
}
}View on GitHub (pinned to ad32144c42)
Solutions
- Check the wrapped error; the sqlite driver names the failing syscall (open: permission denied / read-only file system).
- Verify write access: touch the parent directory as the same user, and confirm the mount is writable.
- Free disk/inodes if the volume is full.
- Point dstPath at an absolute path in a known-writable directory (e.g. /etc/x-ui/ or a fresh backup dir).
Defensive patterns
Strategy: validation
Validate before calling
// ensure the destination path is writable before exporting
f, err := os.OpenFile(dstPath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0o600)
if err != nil { return fmt.Errorf("destination not writable: %w", err) }
f.Close()
os.Remove(dstPath) Prevention
- Write exports to a local writable directory, then move them.
- Check free disk space before large exports.
- Prefer absolute paths for dstPath.
When it happens
Trigger: dstPath on a read-only filesystem or a directory the process cannot write; target file being held open/locked by another process; out of inode/space conditions; dstPath pointing to a directory; CGo sqlite driver unavailable in the build.
Common situations: Running the export in a container with a read-only volume mount; pointing dstPath at a path under a directory owned by root while running as a non-root user; leftover file handles from a previous crashed export on Windows.
Related errors
- open sqlite source: %w
- sqlite backup destination already exists: %s
- source sqlite not found at %s: %w
- destination already exists: %s
- source sqlite not found at %s: %w
AI-assisted analysis of MHSanaei/3x-ui@ad32144c42 (2026-08-15).
Data as JSON: /api/errors/27c7ee05bb311380.
Report an issue: GitHub.