{"record":{"id":"7a7468a1b0b18369","repo":"AdguardTeam/AdGuardHome","slug":"decoding-old-db-w","errorCode":null,"errorMessage":"decoding old db: %w","messagePattern":"decoding old db: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/dhcpd/migrate.go","lineNumber":57,"sourceCode":"// readOldDB reads the old database from the given path.\nfunc readOldDB(path string) (leases []*leaseJSON, err error) {\n\t// #nosec G304 -- Trust this path, since it's taken from the old file name\n\t// relative to the working directory and should generally be considered\n\t// safe.\n\tfile, err := os.Open(path)\n\tif errors.Is(err, os.ErrNotExist) {\n\t\t// Nothing to migrate.\n\t\treturn nil, nil\n\t} else if err != nil {\n\t\t// Don't wrap the error since it's informative enough as is.\n\t\treturn nil, err\n\t}\n\tdefer func() { err = errors.WithDeferred(err, file.Close()) }()\n\n\tleases = []*leaseJSON{}\n\terr = json.NewDecoder(file).Decode(&leases)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"decoding old db: %w\", err)\n\t}\n\n\treturn leases, nil\n}\n\n// migrateDB migrates stored leases if necessary.\nfunc migrateDB(conf *ServerConfig) (err error) {\n\tdefer func() { err = errors.Annotate(err, \"migrating db: %w\") }()\n\n\toldLeasesPath := filepath.Join(conf.WorkDir, dbFilename)\n\tdataDirPath := filepath.Join(conf.DataDir, dataFilename)\n\n\toldLeases, err := readOldDB(oldLeasesPath)\n\tif err != nil {\n\t\t// Don't wrap the error since it's informative enough as is.\n\t\treturn err\n\t} else if oldLeases == nil {\n\t\t// Nothing to migrate.","sourceCodeStart":39,"sourceCodeEnd":75,"githubUrl":"https://github.com/AdguardTeam/AdGuardHome/blob/b41aefbe51c8dde65e2c50f093996afa0502edf9/internal/dhcpd/migrate.go#L39-L75","documentation":"Raised by readOldDB during lease-database migration when the legacy lease file (usually /opt/adguardhome/data/leases.db or workdir equivalent) exists but does not contain valid JSON for []*leaseJSON. Migration aborts, leaving the old file untouched.","triggerScenarios":"Starting AdGuard Home with an old-format lease file that is empty, truncated (crash during a previous write), hand-edited with a syntax error, or in a prehistoric incompatible format.","commonSituations":"Upgrading from a very old AdGuard Home version whose schema differs; a power loss corrupting leases.db mid-write; operators hand-editing the lease file; a zero-byte file left by a failed disk-full write.","solutions":["Back up and inspect the old DB: python3 -m json.tool /path/leases.db to locate the syntax error","If unrecoverable, move leases.db aside (mv leases.db leases.db.bak) — you lose old leases but migration proceeds on next start","Restore from a pre-migration backup if the leases matter","Report the schema upstream if the JSON is valid but structurally different from leaseJSON"],"exampleFix":null,"handlingStrategy":"fallback","validationCode":"if _, err := os.Stat(oldDBPath); err == nil {\n    if b, _ := os.ReadFile(oldDBPath); !json.Valid(b) {\n        // back it up before starting the server\n        os.Rename(oldDBPath, oldDBPath+\".corrupt\")\n    }\n}","typeGuard":null,"tryCatchPattern":"if err := migrateDB(...); err != nil {\n    if strings.Contains(err.Error(), \"decoding old db\") {\n        // preserve old file, continue with empty lease set, restore from backup\n    }\n}","preventionTips":["Back up the workdir (leases.db) before upgrades","Never hand-edit the lease database","Monitor disk space — truncated writes from full disks are the usual corruption source"],"tags":["dhcp","migration","json","database","adguard-home"],"backgroundTag":"corrupt-json-file","analyzedSha":"b41aefbe51c8dde65e2c50f093996afa0502edf9","analyzedAt":"2026-08-27T04:57:55.097Z","schemaVersion":2},"datasetVersion":"2026-08-27T08:17:20.692Z"}