AdguardTeam/AdGuardHome · error

decoding old db: %w

Error message

decoding old db: %w

What it means

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.

Source

Thrown at internal/dhcpd/migrate.go:57

// readOldDB reads the old database from the given path.
func readOldDB(path string) (leases []*leaseJSON, err error) {
	// #nosec G304 -- Trust this path, since it's taken from the old file name
	// relative to the working directory and should generally be considered
	// safe.
	file, err := os.Open(path)
	if errors.Is(err, os.ErrNotExist) {
		// Nothing to migrate.
		return nil, nil
	} else if err != nil {
		// Don't wrap the error since it's informative enough as is.
		return nil, err
	}
	defer func() { err = errors.WithDeferred(err, file.Close()) }()

	leases = []*leaseJSON{}
	err = json.NewDecoder(file).Decode(&leases)
	if err != nil {
		return nil, fmt.Errorf("decoding old db: %w", err)
	}

	return leases, nil
}

// migrateDB migrates stored leases if necessary.
func migrateDB(conf *ServerConfig) (err error) {
	defer func() { err = errors.Annotate(err, "migrating db: %w") }()

	oldLeasesPath := filepath.Join(conf.WorkDir, dbFilename)
	dataDirPath := filepath.Join(conf.DataDir, dataFilename)

	oldLeases, err := readOldDB(oldLeasesPath)
	if err != nil {
		// Don't wrap the error since it's informative enough as is.
		return err
	} else if oldLeases == nil {
		// Nothing to migrate.

View on GitHub (pinned to b41aefbe51)

Solutions

  1. Back up and inspect the old DB: python3 -m json.tool /path/leases.db to locate the syntax error
  2. If unrecoverable, move leases.db aside (mv leases.db leases.db.bak) — you lose old leases but migration proceeds on next start
  3. Restore from a pre-migration backup if the leases matter
  4. Report the schema upstream if the JSON is valid but structurally different from leaseJSON
Defensive patterns

Strategy: fallback

Validate before calling

if _, err := os.Stat(oldDBPath); err == nil {
    if b, _ := os.ReadFile(oldDBPath); !json.Valid(b) {
        // back it up before starting the server
        os.Rename(oldDBPath, oldDBPath+".corrupt")
    }
}

Try / catch

if err := migrateDB(...); err != nil {
    if strings.Contains(err.Error(), "decoding old db") {
        // preserve old file, continue with empty lease set, restore from backup
    }
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of AdguardTeam/AdGuardHome@b41aefbe51 (2026-08-27). Data as JSON: /api/errors/7a7468a1b0b18369. Report an issue: GitHub.