MHSanaei/3x-ui · error
invalid bucket
Error message
invalid bucket
What it means
NodeController.history converts c.Param("bucket") to an int and requires it to be positive AND in service.IsAllowedHistoryBucket's allowed set; otherwise it returns 'invalid bucket' with detail 'unsupported bucket'. Buckets are pre-defined aggregation window sizes (in seconds), not arbitrary numbers, so a numerically valid value can still be rejected.
Source
Thrown at internal/web/controller/node.go:354
}
results, err := a.nodeService.UpdatePanels(req.Ids, req.Dev)
jsonMsgObj(c, I18nWeb(c, "pages.nodes.toasts.updateStarted"), results, err)
}
func (a *NodeController) history(c *gin.Context) {
id, err := strconv.Atoi(c.Param("id"))
if err != nil {
jsonMsg(c, I18nWeb(c, "get"), err)
return
}
metric := c.Param("metric")
if !slices.Contains(service.NodeMetricKeys, metric) {
jsonMsg(c, "invalid metric", fmt.Errorf("unknown metric"))
return
}
bucket, err := strconv.Atoi(c.Param("bucket"))
if err != nil || bucket <= 0 || !service.IsAllowedHistoryBucket(bucket) {
jsonMsg(c, "invalid bucket", fmt.Errorf("unsupported bucket"))
return
}
jsonObj(c, a.nodeService.AggregateNodeMetric(id, metric, bucket, 60), nil)
}
View on GitHub (pinned to ad32144c42)
Solutions
- Use an allowed bucket value — check service.IsAllowedHistoryBucket in your build for the exact set (commonly 60, 300, 900, 3600, …).
- Pass the value as a plain integer path segment, no units.
- After upgrading the panel, reload the frontend so its bucket selector uses the current set.
Example fix
# before GET /panel/api/nodes/history/1/cpu/37 # not an allowed bucket GET /panel/api/nodes/history/1/cpu/1h # not an integer # after GET /panel/api/nodes/history/1/cpu/60
Defensive patterns
Strategy: validation
Validate before calling
const ALLOWED_BUCKETS = [60, 300, 900, 3600] // mirror service.IsAllowedHistoryBucket
if (!ALLOWED_BUCKETS.includes(bucket)) throw new Error(`bucket must be one of ${ALLOWED_BUCKETS}`) Prevention
- Populate the bucket selector from the allowed set, not free entry.
- Pass seconds as plain integers, no units or strings.
- Re-verify the allowed set after each panel upgrade.
When it happens
Trigger: GET /panel/api/nodes/history/:id/:metric/:bucket with bucket=0, a negative number, a non-integer ('1h'), or an integer outside the allowed set (e.g. 37 when only 60/300/… are allowed).
Common situations: Passing minutes instead of seconds; UI dropdown desynced from backend after upgrade; hand-written dashboards guessing the bucket size.
Related errors
AI-assisted analysis of MHSanaei/3x-ui@ad32144c42 (2026-08-15).
Data as JSON: /api/errors/6d05c4408a8e226b.
Report an issue: GitHub.