AdguardTeam/AdGuardHome · error

parsing expiry time

Error message

parsing expiry time: %w

What it means

A stored dynamic lease's Expiry field is not a valid RFC3339 timestamp, so it cannot be converted to time.Time. Static leases are exempt (expiry skipped when IsStatic is true).

Solutions

  1. Fix expiry values to RFC3339 format with timezone (2026-01-02T15:04:05Z)
  2. Remove the leases file to force re-learning of dynamic leases
  3. Check for and fix truncated/corrupted files

Example fix

// before
{"Expiry": "2026-01-02 15:04:05"}
// after
{"Expiry": "2026-01-02T15:04:05Z"}
Defensive patterns

Strategy: validation

Validate before calling

if !dl.IsStatic {
    if _, err := time.Parse(time.RFC3339, dl.Expiry); err != nil { /* drop lease */ }
}

Type guard

func isRFC3339(s string) bool { _, err := time.Parse(time.RFC3339, s); return err == nil }

Prevention

When it happens

Trigger: Loading the lease DB where a non-static lease has an empty, truncated, or non-RFC3339 Expiry string (e.g. missing timezone offset).

Common situations: Hand-edited lease files, corruption, or a version change in date format; expiry set to 0 or unix epoch integer instead of RFC3339.

Related errors


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

Appendix: source

Thrown at internal/dhcpd/db.go:79

		Hostname: l.Hostname,
		HWAddr:   l.HWAddr.String(),
		IP:       l.IP,
		IsStatic: l.IsStatic,
	}
}

// toLease converts *dbLease to *dhcpsvc.Lease.
func (dl *dbLease) toLease() (l *dhcpsvc.Lease, err error) {
	mac, err := net.ParseMAC(dl.HWAddr)
	if err != nil {
		return nil, fmt.Errorf("parsing hardware address: %w", err)
	}

	expiry := time.Time{}
	if !dl.IsStatic {
		expiry, err = time.Parse(time.RFC3339, dl.Expiry)
		if err != nil {
			return nil, fmt.Errorf("parsing expiry time: %w", err)
		}
	}

	return &dhcpsvc.Lease{
		Expiry:   expiry,
		IP:       dl.IP,
		Hostname: dl.Hostname,
		HWAddr:   mac,
		IsStatic: dl.IsStatic,
	}, nil
}

// dbLoad loads stored leases.
func (s *server) dbLoad() (err error) {
	data, err := os.ReadFile(s.conf.dbFilePath)
	if err != nil {
		if !errors.Is(err, os.ErrNotExist) {
			return fmt.Errorf("reading db: %w", err)

View on GitHub (pinned to b41aefbe51)