{"record":{"id":"ca0cad6e7e889637","repo":"IceWhaleTech/CasaOS","slug":"sqlite-connect-error","errorCode":null,"errorMessage":"sqlite connect error","messagePattern":"sqlite connect error","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"pkg/sqlite/db.go","lineNumber":35,"sourceCode":"\t\"github.com/IceWhaleTech/CasaOS/pkg/utils/file\"\n\tmodel2 \"github.com/IceWhaleTech/CasaOS/service/model\"\n\t\"github.com/glebarez/sqlite\"\n\t\"gorm.io/gorm\"\n)\n\nvar gdb *gorm.DB\n\nfunc GetDb(dbPath string) *gorm.DB {\n\tif gdb != nil {\n\t\treturn gdb\n\t}\n\t// Refer https://github.com/go-sql-driver/mysql#dsn-data-source-name\n\t// dsn := fmt.Sprintf(\"%v:%v@tcp(%v:%v)/%v?charset=utf8mb4&parseTime=True&loc=Local\", m.User, m.PWD, m.IP, m.Port, m.DBName)\n\t// db, err := gorm.Open(mysql2.Open(dsn), &gorm.Config{})\n\tfile.IsNotExistMkDir(dbPath)\n\tdb, err := gorm.Open(sqlite.Open(dbPath+\"/casaOS.db\"), &gorm.Config{})\n\tif err != nil {\n\t\tpanic(\"sqlite connect error\")\n\t}\n\n\tc, _ := db.DB()\n\tc.SetMaxIdleConns(10)\n\tc.SetMaxOpenConns(1)\n\tc.SetConnMaxIdleTime(time.Second * 1000)\n\tgdb = db\n\n\terr = db.AutoMigrate(&model2.AppNotify{}, model2.SharesDBModel{}, model2.ConnectionsDBModel{}, model2.PeerDriveDBModel{})\n\tif err != nil {\n\t\tfmt.Println(err)\n\t}\n\n\tdb.Exec(\"DROP TABLE IF EXISTS o_application\")\n\tdb.Exec(\"DROP TABLE IF EXISTS o_friend\")\n\tdb.Exec(\"DROP TABLE IF EXISTS o_person_download\")\n\tdb.Exec(\"DROP TABLE IF EXISTS o_person_down_record\")\n\treturn db","sourceCodeStart":17,"sourceCodeEnd":53,"githubUrl":"https://github.com/IceWhaleTech/CasaOS/blob/0d3b2f444ec0193193cf03eef6d43c6e35b0183e/pkg/sqlite/db.go#L17-L53","documentation":"A panic raised by GetDb() in pkg/sqlite/db.go when gorm.Open(sqlite.Open(...)) fails to open the SQLite database file at dbPath/casaOS.db. Unlike the HTTP errors, this is a process-killing panic, not a returned error: any caller that triggers the first initialization on a bad path crashes the whole CasaOS binary. Typical root causes are an unwritable/missing directory, a corrupted database file, or file locking conflicts from concurrent access (SQLite allows one writer).","triggerScenarios":"(1) dbPath directory has wrong ownership/permissions (common when the data dir was moved or restored), (2) casaOS.db is corrupt from an unclean power-off (no WAL checkpoint completed), (3) another CasaOS instance or process holds the file lock, (4) disk full so SQLite cannot create its journal, (5) dbPath on a filesystem (some NFS/FUSE mounts) without working POSIX locks.","commonSituations":"First start after OS reinstall with an old /DATA partition preserved; SD-card wear causing a truncated db file; running two CasaOS versions simultaneously during an upgrade; Docker bind-mount with read-only volume for /var/lib/casaos; migration tool copying the db with wrong uid.","solutions":["Check permissions/ownership of dbPath (ls -ld) and chown it to the user running CasaOS; ensure it is writable.","Check disk space (df -h) and free space so SQLite can write its journal/WAL.","If another instance holds the file, stop it (only one CasaOS may own casaOS.db), or move the stale lock aside.","If the db is corrupt, back it up and delete casaOS.db so AutoMigrate rebuilds a fresh schema (accepting loss of notify/shares/connections tables).","Longer term, replace panic with a returned error or a logged fatal that includes the gorm err so the cause is visible."],"exampleFix":"// before\ndb, err := gorm.Open(sqlite.Open(dbPath+\"/casaOS.db\"), &gorm.Config{})\nif err != nil {\n    panic(\"sqlite connect error\")\n}\n\n// after\ndb, err := gorm.Open(sqlite.Open(dbPath+\"/casaOS.db\"), &gorm.Config{})\nif err != nil {\n    logger.Error(\"sqlite connect error\", zap.String(\"path\", dbPath), zap.Error(err))\n    panic(fmt.Sprintf(\"sqlite connect error: %v\", err))\n}","handlingStrategy":"validation","validationCode":"// before first GetDb() call\ninfo, err := os.Stat(dbPath)\nif err != nil || !info.IsDir() {\n    return fmt.Errorf(\"db path %s missing or not a directory\", dbPath)\n}\nif err := unix.Access(dbPath, unix.W_OK); err != nil {\n    return fmt.Errorf(\"db path %s not writable\", dbPath)\n}\ndisk := syscall.Statfs_t{}\nif err := syscall.Statfs(dbPath, &disk); err == nil && disk.Bavail*uint64(disk.Bsize) < 10<<20 {\n    return fmt.Errorf(\"less than 10MB free for sqlite db\")\n}","typeGuard":null,"tryCatchPattern":"// GetDb panics, so guard at process level if you embed CasaOS code:\nfunc safeDbInit(path string) (db *gorm.DB, err error) {\n    defer func() {\n        if r := recover(); r != nil {\n            err = fmt.Errorf(\"sqlite init panic: %v\", r)\n        }\n    }()\n    return sqlite.GetDb(path), nil\n}","preventionTips":["Validate the data directory exists, is writable, and has free space before first use.","Run exactly one CasaOS instance per casaOS.db — SQLite allows a single writer (the code even sets MaxOpenConns(1)).","Keep the db on a local filesystem with POSIX locks; avoid NFS/FUSE-only mounts.","Back up casaOS.db before upgrades so corruption can be recovered by restore rather than rebuild."],"tags":["sqlite","gorm","panic","database","filesystem","casaos"],"backgroundTag":null,"analyzedSha":"0d3b2f444ec0193193cf03eef6d43c6e35b0183e","analyzedAt":"2026-08-15T13:27:57.821Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}