AdguardTeam/AdGuardHome · error
%s: must be a multiple of 1 hour
Error message
%s: must be a multiple of 1 hour
What it means
The stats HTTP handler requires the 'recent' query parameter (milliseconds) to be a whole number of hours. This error is returned when recentMs % 3600000 != 0.
Source
Thrown at internal/stats/http.go:118
// the parameter is empty, the original limit is returned.
func parseRecent(recent string, limit time.Duration) (parsedLimit time.Duration, err error) {
if recent == "" {
return limit, nil
}
recentMs, err := strconv.ParseInt(recent, 10, 64)
if err != nil {
return 0, fmt.Errorf("%s: parsing interval: %s", queryKeyRecent, err)
}
err = validate.InRange(queryKeyRecent, recentMs, millisecondsInHour, limit.Milliseconds())
if err != nil {
// Don't wrap the error since it's already informative enough as is.
return 0, err
}
if recentMs%millisecondsInHour != 0 {
return 0, fmt.Errorf("%s: must be a multiple of 1 hour", queryKeyRecent)
}
return time.Duration(recentMs) * time.Millisecond, nil
}
// configResp is the response to the GET /control/stats_info.
type configResp struct {
IntervalDays uint32 `json:"interval"`
}
// getConfigResp is the response to the GET /control/stats_info.
type getConfigResp struct {
// Ignored is the list of host names, which should not be counted.
Ignored []string `json:"ignored"`
// Interval is the statistics rotation interval in milliseconds.
Interval float64 `json:"interval"`
View on GitHub (pinned to b41aefbe51)
Solutions
- Round the desired duration up/down to whole hours before sending
- Send e.g. recent=3600000 for 1 hour
- Omit recent to use the configured default limit
Example fix
// before recent := 30 * time.Minute // after recent := 1 * time.Hour
Defensive patterns
Strategy: validation
Validate before calling
ms := d.Milliseconds()
ms = ms / 3600000 * 3600000 // round down to whole hours
if ms <= 0 { ms = 3600000 } Type guard
func isWholeHours(ms int64) bool { return ms%3600000 == 0 } Prevention
- Snap durations to whole hours client-side
- Document the hourly constraint in client wrappers
When it happens
Trigger: Calling /control/stats with a recent value that is not a multiple of one hour in milliseconds, e.g. recent=1800000 (30 minutes) or recent=1.
Common situations: Clients computing recent from arbitrary durations (minutes, seconds) or passing 1 meaning '1 hour'; stale clients from before the hourly constraint was added.
Related errors
- %s: parsing interval: %s
- json time is nil
- interface %s has no ipv6 addresses
- invalid ip version %d
- unknown result code %d
AI-assisted analysis of AdguardTeam/AdGuardHome@b41aefbe51 (2026-08-27).
Data as JSON: /api/errors/a5bc0f567096134f.
Report an issue: GitHub.