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

  1. Use an allowed bucket value — check service.IsAllowedHistoryBucket in your build for the exact set (commonly 60, 300, 900, 3600, …).
  2. Pass the value as a plain integer path segment, no units.
  3. 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

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.