{"record":{"id":"e634cb70682504c1","repo":"XTLS/Xray-core","slug":"burstobservatory-requires-a-valid-pingconfig","errorCode":null,"errorMessage":"BurstObservatory requires a valid pingConfig","messagePattern":"BurstObservatory requires a valid pingConfig","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"infra/conf/observatory.go","lineNumber":31,"sourceCode":"\tSubjectSelector   []string          `json:\"subjectSelector\"`\n\tProbeURL          string            `json:\"probeURL\"`\n\tProbeInterval     duration.Duration `json:\"probeInterval\"`\n\tEnableConcurrency bool              `json:\"enableConcurrency\"`\n}\n\nfunc (o *ObservatoryConfig) Build() (proto.Message, error) {\n\treturn &observatory.Config{SubjectSelector: o.SubjectSelector, ProbeUrl: o.ProbeURL, ProbeInterval: int64(o.ProbeInterval), EnableConcurrency: o.EnableConcurrency}, nil\n}\n\ntype BurstObservatoryConfig struct {\n\tSubjectSelector []string `json:\"subjectSelector\"`\n\t// health check settings\n\tHealthCheck *healthCheckSettings `json:\"pingConfig,omitempty\"`\n}\n\nfunc (b BurstObservatoryConfig) Build() (proto.Message, error) {\n\tif b.HealthCheck == nil {\n\t\treturn nil, errors.New(\"BurstObservatory requires a valid pingConfig\")\n\t}\n\tif result, err := b.HealthCheck.Build(); err == nil {\n\t\treturn &burst.Config{SubjectSelector: b.SubjectSelector, PingConfig: result.(*burst.HealthPingConfig)}, nil\n\t} else {\n\t\treturn nil, err\n\t}\n}\n","sourceCodeStart":13,"sourceCodeEnd":39,"githubUrl":"https://github.com/XTLS/Xray-core/blob/7d214f8b094f75322fa3990f8aadad1c912f24f5/infra/conf/observatory.go#L13-L39","documentation":"Thrown by BurstObservatoryConfig.Build when the burst observatory block has no pingConfig (JSON key pingConfig, Go field HealthCheck). The burst observatory performs concurrent health checks whose parameters (URL, interval, connectivity check, sampling) all live in pingConfig; without it there is nothing to drive health measurement, so build fails. Note the health check settings builder's own errors are propagated unwrapped when pingConfig exists but is invalid.","triggerScenarios":"Adding an observatory entry of type burst without a pingConfig object, e.g. {\"subjectSelector\":[\"out\"],\"type\":\"burst\"} with no pingConfig key.","commonSituations":"Converting a plain (non-burst) observatory config to burst and forgetting that burst mandates pingConfig; trimming config to 'required' fields by mistake.","solutions":["Add a pingConfig object to the burst observatory entry (probeURL, interval, connectivityTimeout, sampling, etc.)","If you do not need health-ping-driven decisions, use the regular observatory instead of burst"],"exampleFix":"// before\n{ \"type\": \"burst\", \"subjectSelector\": [\"out\"] }\n// after\n{ \"type\": \"burst\", \"subjectSelector\": [\"out\"], \"pingConfig\": { \"destination\": \"https://www.google.com/generate_204\", \"interval\": \"1m\", \"connectivity\": \"https://www.google.com/generate_204\", \"timeout\": \"5s\", \"sampling\": 2 } }","handlingStrategy":"validation","validationCode":"if strings.EqualFold(obs.Type, \"burst\") && obs.PingConfig == nil {\n\treturn errors.New(\"burst observatory requires pingConfig\")\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Treat pingConfig as required in burst observatory templates","Use the non-burst observatory when health-ping settings are not needed"],"tags":["xray","config","observatory","burst","health-check","validation"],"backgroundTag":null,"analyzedSha":"7d214f8b094f75322fa3990f8aadad1c912f24f5","analyzedAt":"2026-08-15T14:26:24.325Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}