{"record":{"id":"2530d8dd2ce690a1","repo":"theonedev/onedev","slug":"unexpected-operator-operatortext","errorCode":null,"errorMessage":"Unexpected operator: ${operatorText}","messagePattern":"Unexpected operator: (.+?)","errorType":"validation","errorClass":"ExplicitException","httpStatus":500,"severity":"error","filePath":"server-core/src/main/java/io/onedev/server/search/buildmetric/BuildMetricQuery.java","lineNumber":67,"sourceCode":"\t\t\tCommonTokenStream tokens = new CommonTokenStream(lexer);\n\t\t\tBuildMetricQueryParser parser = new BuildMetricQueryParser(tokens);\n\t\t\tparser.removeErrorListeners();\n\t\t\tparser.setErrorHandler(new BailErrorStrategy());\n\t\t\tQueryContext queryContext = parser.query();\n\t\t\tCriteriaContext criteriaContext = queryContext.criteria();\n\t\t\tBuildMetricCriteria metricCriteria;\n\t\t\tif (criteriaContext != null) {\n\t\t\t\tmetricCriteria = new BuildMetricQueryBaseVisitor<BuildMetricCriteria>() {\n\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic BuildMetricCriteria visitOperatorCriteria(OperatorCriteriaContext ctx) {\n\t\t\t\t\t\tswitch (ctx.operator.getType()) {\n\t\t\t\t\t\tcase BuildIsSuccessful:\n\t\t\t\t\t\t\treturn new BuildIsSuccessfulCriteria();\n\t\t\t\t\t\tcase BuildIsFailed:\n\t\t\t\t\t\t\treturn new BuildIsFailedCriteria();\n\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\tthrow new ExplicitException(\"Unexpected operator: \" + ctx.operator.getText());\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\t\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic BuildMetricCriteria visitParensCriteria(ParensCriteriaContext ctx) {\n\t\t\t\t\t\treturn (BuildMetricCriteria) visit(ctx.criteria()).withParens(true);\n\t\t\t\t\t}\n\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic BuildMetricCriteria visitFieldOperatorCriteria(FieldOperatorCriteriaContext ctx) {\n\t\t\t\t\t\tString fieldName = QueryUtils.getValue(ctx.Quoted().getText());\n\t\t\t\t\t\tint operator = ctx.operator.getType();\n\t\t\t\t\t\tcheckField(project, fieldName, operator);\n\t\t\t\t\t\tif (fieldName.equals(NAME_PULL_REQUEST))\n\t\t\t\t\t\t\treturn new PullRequestEmptyCriteria(operator);\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\treturn new ParamEmptyCriteria(fieldName, operator);\n\t\t\t\t\t}","sourceCodeStart":49,"sourceCodeEnd":85,"githubUrl":"https://github.com/theonedev/onedev/blob/d44925c47c37992c828ea673a5f9620539bc3ff2/server-core/src/main/java/io/onedev/server/search/buildmetric/BuildMetricQuery.java#L49-L85","documentation":"While translating a parsed build metric query AST, the visitor's visitOperatorCriteria only accepts BuildIsSuccessful and BuildIsFailed operator tokens; anything else triggers ExplicitException('Unexpected operator: ...'). The grammar allowed a token that the criteria builder does not support.","triggerScenarios":"Using an operator token in a build metric query that parses but has no criteria mapping (e.g. comparison operators like >, <, ~, or operators copied from other OneDev query languages such as issue/build queries).","commonSituations":"Users reusing operators from issue or build query languages in build metric queries; grammar updated with new operators while the visitor was not extended (version mismatch); typos like 'issuccessful' mapping to an unexpected token.","solutions":["Restrict the metric query to supported operators: 'status is successful' / 'status is failed' (BuildIsSuccessful / BuildIsFailed).","Check the OneDev version: if the grammar added operators, upgrade the code so visitOperatorCriteria maps them to criteria classes.","Catch ExplicitException in the query UI/API layer and show the list of valid operators to the user.","Extend the switch in BuildMetricQuery's visitor with a case for the new operator returning the appropriate BuildMetricCriteria."],"exampleFix":"// before\ndefault:\n    throw new ExplicitException(\"Unexpected operator: \" + ctx.operator.getText());\n// after\ncase BuildIsSuccessful:\n    return new BuildIsSuccessfulCriteria();\ncase BuildIsFailed:\n    return new BuildIsFailedCriteria();\ncase BuildFinished:\n    return new BuildFinishedCriteria(); // newly supported operator\ndefault:\n    throw new ExplicitException(\"Unexpected operator: \" + ctx.operator.getText());","handlingStrategy":"try-catch","validationCode":"Set<String> allowed = Set.of(\"is successful\", \"is failed\");\n// pre-validate operator usage in the raw query string before parsing","typeGuard":null,"tryCatchPattern":"try {\n    BuildMetricQuery q = BuildMetricQuery.parse(input);\n} catch (ExplicitException e) {\n    if (e.getMessage().startsWith(\"Unexpected operator\"))\n        throw new ExplicitException(\"Unsupported operator; use 'is successful' or 'is failed'\");\n    throw e;\n}","preventionTips":["Use only BuildIsSuccessful/BuildIsFailed operators in build metric queries","Do not copy operators from issue/build query languages into metric queries","When extending the grammar, always add a matching case in visitOperatorCriteria"],"tags":["query","parser","operator","build-metrics","unsupported"],"backgroundTag":"unsupported-enum-value","analyzedSha":"d44925c47c37992c828ea673a5f9620539bc3ff2","analyzedAt":"2026-09-06T07:18:27.995Z","contentChangedAt":"2026-09-06T07:18:27.995Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}