{"record":{"id":"5ec7fad1bf043ffa","repo":"SigNoz/signoz","slug":"all-queries-must-have-metric-data-source","errorCode":null,"errorMessage":"all queries must have metric data source","messagePattern":"all queries must have metric data source","errorType":"validation","errorClass":"model.ApiError","httpStatus":400,"severity":"warning","filePath":"ee/query-service/app/api/queryrange.go","lineNumber":76,"sourceCode":"\t\t\tfor _, fn := range query.Functions {\n\t\t\t\tif fn.Name == v3.FunctionNameAnomaly {\n\t\t\t\t\tanomalyQueryExists = true\n\t\t\t\t\tanomalyQuery = query\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tif anomalyQueryExists {\n\t\t// ensure all queries have metric data source, and there should be only one anomaly query\n\t\tfor _, query := range queryRangeParams.CompositeQuery.BuilderQueries {\n\t\t\t// What is query.QueryName == query.Expression doing here?\n\t\t\t// In the current implementation, the way to recognize if a query is a formula is by\n\t\t\t// checking if the expression is the same as the query name. if the expression is different\n\t\t\t// then it is a formula. otherwise, it is simple builder query.\n\t\t\tif query.DataSource != v3.DataSourceMetrics && query.QueryName == query.Expression {\n\t\t\t\tRespondError(w, &model.ApiError{Typ: model.ErrorBadData, Err: fmt.Errorf(\"all queries must have metric data source\")}, nil)\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\n\t\t// get the threshold, and seasonality from the anomaly query\n\t\tvar seasonality anomaly.Seasonality\n\t\tfor _, fn := range anomalyQuery.Functions {\n\t\t\tif fn.Name == v3.FunctionNameAnomaly {\n\t\t\t\tseasonalityStr, ok := fn.NamedArgs[\"seasonality\"].(string)\n\t\t\t\tif !ok {\n\t\t\t\t\tseasonalityStr = \"daily\"\n\t\t\t\t}\n\t\t\t\tif seasonalityStr == \"weekly\" {\n\t\t\t\t\tseasonality = anomaly.SeasonalityWeekly\n\t\t\t\t} else if seasonalityStr == \"daily\" {\n\t\t\t\t\tseasonality = anomaly.SeasonalityDaily\n\t\t\t\t} else {\n\t\t\t\t\tseasonality = anomaly.SeasonalityHourly","sourceCodeStart":58,"sourceCodeEnd":94,"githubUrl":"https://github.com/SigNoz/signoz/blob/5069bf80b08f1f00d7e014eccc09902f9871004f/ee/query-service/app/api/queryrange.go#L58-L94","documentation":"In the anomaly-query path of queryRangeV4, every builder query that is not a formula (QueryName == Expression) must use the metrics data source. Any such plain builder query with DataSource != v3.DataSourceMetrics is rejected as bad data before execution.","triggerScenarios":"POSTing a v4 query range request with an anomaly query whose builder queries include a logs or traces data-source query that is not a formula.","commonSituations":"Building anomaly dashboards and accidentally mixing log/traces panels, or programmatically generated composite queries defaulting to the wrong DataSource.","solutions":["Set DataSource: v3.DataSourceMetrics on all non-formula builder queries in the request","Remove the offending logs/traces queries from the anomaly composite query","Validate the composite query client-side before submitting"],"exampleFix":"// before\n{\"queryName\": \"A\", \"expression\": \"A\", \"dataSource\": \"logs\"}\n// after\n{\"queryName\": \"A\", \"expression\": \"A\", \"dataSource\": \"metrics\"}","handlingStrategy":"validation","validationCode":"for name, q := range params.CompositeQuery.BuilderQueries {\n    if q.DataSource != v3.DataSourceMetrics && q.QueryName == q.Expression {\n        return fmt.Errorf(\"query %s: anomaly queries require metrics data source\", name)\n    }\n}","typeGuard":null,"tryCatchPattern":"Not applicable — reject the request body before submission using the same rule the server enforces.","preventionTips":["Default new builder queries to metrics in anomaly UIs","Lint composite queries in CI for dashboards"],"tags":["query-validation","anomaly","metrics","http-api"],"backgroundTag":"query-validation-failed","analyzedSha":"5069bf80b08f1f00d7e014eccc09902f9871004f","analyzedAt":"2026-08-28T06:22:12.824Z","schemaVersion":2},"datasetVersion":"2026-08-28T11:17:15.048Z"}