{"record":{"id":"27c7ee05bb311380","repo":"MHSanaei/3x-ui","slug":"open-sqlite-destination-w","errorCode":null,"errorMessage":"open sqlite destination: %w","messagePattern":"open sqlite destination: %w","errorType":"console","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/database/migrate_data.go","lineNumber":181,"sourceCode":"\t// Start from an empty file so AutoMigrate creates the canonical schema.\n\tif err := os.Remove(dstPath); err != nil && !os.IsNotExist(err) {\n\t\treturn err\n\t}\n\n\tsrc, err := gorm.Open(postgres.Open(srcDSN), &gorm.Config{Logger: logger.Discard})\n\tif err != nil {\n\t\treturn fmt.Errorf(\"open postgres 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\t// No WAL: keep all data in the main file so it is complete once closed.\n\tdst, err := gorm.Open(sqlite.Open(dstPath+\"?_busy_timeout=10000\"), &gorm.Config{Logger: logger.Discard})\n\tif err != nil {\n\t\treturn fmt.Errorf(\"open sqlite 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\n\treturn copyAllModels(src, dst)\n}\n\n// copyAllModels (re)creates the schema on dst and copies every migrated table\n// from src to dst in FK-safe order. src/dst may be any gorm backend.\nfunc copyAllModels(src, dst *gorm.DB) error {\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}","sourceCodeStart":163,"sourceCodeEnd":199,"githubUrl":"https://github.com/MHSanaei/3x-ui/blob/ad32144c42455696ea9f14e12168beac3e25f5d2/internal/database/migrate_data.go#L163-L199","documentation":"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).","triggerScenarios":"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.","commonSituations":"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.","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)."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// ensure the destination path is writable before exporting\nf, err := os.OpenFile(dstPath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0o600)\nif err != nil { return fmt.Errorf(\"destination not writable: %w\", err) }\nf.Close()\nos.Remove(dstPath)","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Write exports to a local writable directory, then move them.","Check free disk space before large exports.","Prefer absolute paths for dstPath."],"tags":["database","sqlite","filesystem","migration"],"backgroundTag":null,"analyzedSha":"ad32144c42455696ea9f14e12168beac3e25f5d2","analyzedAt":"2026-08-15T11:13:23.905Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}