{"record":{"id":"d9896fcd234156ac","repo":"MHSanaei/3x-ui","slug":"invalid-tag-unknown-observatory-tag","errorCode":null,"errorMessage":"invalid tag (unknown observatory tag)","messagePattern":"invalid tag \\(unknown observatory tag\\)","errorType":"http","errorClass":null,"httpStatus":200,"severity":"error","filePath":"internal/web/controller/server.go","lineNumber":174,"sourceCode":"\tif !slices.Contains(service.XrayMetricKeys, metric) {\n\t\tjsonMsg(c, \"invalid metric\", fmt.Errorf(\"unknown metric\"))\n\t\treturn\n\t}\n\tbucket, ok := parseHistoryBucket(c)\n\tif !ok {\n\t\treturn\n\t}\n\tjsonObj(c, a.xrayMetricsService.AggregateMetric(metric, bucket, 60), nil)\n}\n\nfunc (a *ServerController) getXrayObservatory(c *gin.Context) {\n\tjsonObj(c, a.xrayMetricsService.ObservatorySnapshot(), nil)\n}\n\nfunc (a *ServerController) getXrayObservatoryHistoryBucket(c *gin.Context) {\n\ttag := c.Param(\"tag\")\n\tif !a.xrayMetricsService.HasObservatoryTag(tag) {\n\t\tjsonMsg(c, \"invalid tag\", fmt.Errorf(\"unknown observatory tag\"))\n\t\treturn\n\t}\n\tbucket, ok := parseHistoryBucket(c)\n\tif !ok {\n\t\treturn\n\t}\n\tjsonObj(c, a.xrayMetricsService.AggregateObservatory(tag, bucket, 60), nil)\n}\n\nfunc (a *ServerController) getXrayVersion(c *gin.Context) {\n\tversions, err := a.serverService.GetXrayVersionsCached()\n\tif err != nil {\n\t\tjsonMsg(c, I18nWeb(c, \"getVersion\"), err)\n\t\treturn\n\t}\n\tjsonObj(c, versions, nil)\n}\n","sourceCodeStart":156,"sourceCodeEnd":192,"githubUrl":"https://github.com/MHSanaei/3x-ui/blob/ad32144c42455696ea9f14e12168beac3e25f5d2/internal/web/controller/server.go#L156-L192","documentation":"ServerController.getXrayObservatoryHistoryBucket validates c.Param(\"tag\") via xrayMetricsService.HasObservatoryTag — only tags actually registered from Xray's burst-observatory outbound config are accepted. 'invalid tag' (detail 'unknown observatory tag') means the tag is not among the observed outbound tags, either mistyped or the observatory feature not configured/started.","triggerScenarios":"GET /panel/api/server/history/observatory/:tag/:bucket with a tag that does not match any outbound tag under the observatory config — e.g. 'proxy-1' when the outbound tag is 'proxy1', or any tag when the panel runs no burst observatory.","commonSituations":"Outbound tag renamed in config without updating dashboards; observatory disabled or Xray not running so no tags are registered; a fresh panel before any observatory data arrives.","solutions":["First fetch GET /panel/api/server/xrayObservatory (ObservatorySnapshot) and use a tag that appears there.","Align the tag with the outbound's tag field in your Xray config exactly (case-sensitive).","If the snapshot is empty, enable/configure the observatory in the panel and restart Xray so tags register."],"exampleFix":"# before\nGET /panel/api/server/history/observatory/proxy-1/60   # invalid tag\n\n# after\ncurl /panel/api/server/xrayObservatory         # see real tags, e.g. \"proxy1\"\nGET /panel/api/server/history/observatory/proxy1/60","handlingStrategy":"validation","validationCode":"const snap = await fetch('/panel/api/server/xrayObservatory').then(r => r.json())\nconst validTags = new Set(Object.keys(snap?.success?.tags ?? snap?.obj?.tags ?? {}))\nif (!validTags.has(tag)) throw new Error(`unknown observatory tag ${tag}`)","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always enumerate live tags via the observatory snapshot before requesting history.","Keep outbound tags stable and case-sensitive-identical across config and dashboards.","Treat an empty snapshot as 'observatory not configured' rather than 'bad tag'."],"tags":["api","http-400","validation","xray","observatory"],"backgroundTag":null,"analyzedSha":"ad32144c42455696ea9f14e12168beac3e25f5d2","analyzedAt":"2026-08-15T11:13:23.905Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}