MHSanaei/3x-ui · error
invalid metric (unknown metric)
Error message
invalid metric (unknown metric)
What it means
ServerController.getXrayMetricsHistoryBucket validates its metric path parameter against service.XrayMetricKeys — a different allowlist from the system metrics — and returns 'invalid metric' (detail 'unknown metric') on mismatch. These keys describe Xray child-process counters exposed by the xray metrics service, so the accepted names depend on what that service tracks.
Source
Thrown at internal/web/controller/server.go:157
if !slices.Contains(service.SystemMetricKeys, metric) {
jsonMsg(c, "invalid metric", fmt.Errorf("unknown metric"))
return
}
bucket, ok := parseHistoryBucket(c)
if !ok {
return
}
jsonObj(c, a.serverService.AggregateSystemMetric(metric, bucket, 60), nil)
}
func (a *ServerController) getXrayMetricsState(c *gin.Context) {
jsonObj(c, a.xrayMetricsService.State(), nil)
}
func (a *ServerController) getXrayMetricsHistoryBucket(c *gin.Context) {
metric := c.Param("metric")
if !slices.Contains(service.XrayMetricKeys, metric) {
jsonMsg(c, "invalid metric", fmt.Errorf("unknown metric"))
return
}
bucket, ok := parseHistoryBucket(c)
if !ok {
return
}
jsonObj(c, a.xrayMetricsService.AggregateMetric(metric, bucket, 60), nil)
}
func (a *ServerController) getXrayObservatory(c *gin.Context) {
jsonObj(c, a.xrayMetricsService.ObservatorySnapshot(), nil)
}
func (a *ServerController) getXrayObservatoryHistoryBucket(c *gin.Context) {
tag := c.Param("tag")
if !a.xrayMetricsService.HasObservatoryTag(tag) {
jsonMsg(c, "invalid tag", fmt.Errorf("unknown observatory tag"))
returnView on GitHub (pinned to ad32144c42)
Solutions
- Use only keys from service.XrayMetricKeys for this endpoint (check the var in your build).
- Do not assume system metric names carry over — enumerate the correct list for /history/xray/.
- After upgrading Xray-core or the panel, re-check the key set for renames.
Example fix
# before GET /panel/api/server/history/xray/mem/60 # system key on xray endpoint -> invalid metric # after GET /panel/api/server/history/xray/<key from service.XrayMetricKeys>/60
Defensive patterns
Strategy: type-guard
Validate before calling
// keep in sync with service.XrayMetricKeys in the deployed build
const XRAY_METRIC_KEYS = [...] as const // copy from source
function isXrayMetric(m: string): m is (typeof XRAY_METRIC_KEYS)[number] {
return (XRAY_METRIC_KEYS as readonly string[]).includes(m)
} Type guard
function isXrayMetric(m: string): m is (typeof XRAY_METRIC_KEYS)[number] {
return (XRAY_METRIC_KEYS as readonly string[]).includes(m)
} Prevention
- Never reuse system-metric key lists on the /history/xray/ endpoints.
- Regenerate API client constants after upgrading the panel.
- Pin dashboards to a known panel version or fetch key lists dynamically.
When it happens
Trigger: GET /panel/api/server/history/xray/:metric/:bucket with a system-metric name ('cpu') or an invented Xray counter name instead of one of service.XrayMetricKeys.
Common situations: Confusing the system-metric endpoint's keys with the Xray-metric endpoint's keys; version change renaming Xray counters; scripts reused across endpoint families.
Related errors
AI-assisted analysis of MHSanaei/3x-ui@ad32144c42 (2026-08-15).
Data as JSON: /api/errors/a607fa4fc4a57849.
Report an issue: GitHub.