theonedev/onedev · error · ExplicitException
Unexpected operator: ${operatorText}
Error message
Unexpected operator: ${operatorText} What it means
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.
Source
Thrown at server-core/src/main/java/io/onedev/server/search/buildmetric/BuildMetricQuery.java:67
CommonTokenStream tokens = new CommonTokenStream(lexer);
BuildMetricQueryParser parser = new BuildMetricQueryParser(tokens);
parser.removeErrorListeners();
parser.setErrorHandler(new BailErrorStrategy());
QueryContext queryContext = parser.query();
CriteriaContext criteriaContext = queryContext.criteria();
BuildMetricCriteria metricCriteria;
if (criteriaContext != null) {
metricCriteria = new BuildMetricQueryBaseVisitor<BuildMetricCriteria>() {
@Override
public BuildMetricCriteria visitOperatorCriteria(OperatorCriteriaContext ctx) {
switch (ctx.operator.getType()) {
case BuildIsSuccessful:
return new BuildIsSuccessfulCriteria();
case BuildIsFailed:
return new BuildIsFailedCriteria();
default:
throw new ExplicitException("Unexpected operator: " + ctx.operator.getText());
}
}
@Override
public BuildMetricCriteria visitParensCriteria(ParensCriteriaContext ctx) {
return (BuildMetricCriteria) visit(ctx.criteria()).withParens(true);
}
@Override
public BuildMetricCriteria visitFieldOperatorCriteria(FieldOperatorCriteriaContext ctx) {
String fieldName = QueryUtils.getValue(ctx.Quoted().getText());
int operator = ctx.operator.getType();
checkField(project, fieldName, operator);
if (fieldName.equals(NAME_PULL_REQUEST))
return new PullRequestEmptyCriteria(operator);
else
return new ParamEmptyCriteria(fieldName, operator);
}View on GitHub (pinned to d44925c47c)
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.
Example fix
// before
default:
throw new ExplicitException("Unexpected operator: " + ctx.operator.getText());
// after
case BuildIsSuccessful:
return new BuildIsSuccessfulCriteria();
case BuildIsFailed:
return new BuildIsFailedCriteria();
case BuildFinished:
return new BuildFinishedCriteria(); // newly supported operator
default:
throw new ExplicitException("Unexpected operator: " + ctx.operator.getText()); Defensive patterns
Strategy: try-catch
Validate before calling
Set<String> allowed = Set.of("is successful", "is failed");
// pre-validate operator usage in the raw query string before parsing Try / catch
try {
BuildMetricQuery q = BuildMetricQuery.parse(input);
} catch (ExplicitException e) {
if (e.getMessage().startsWith("Unexpected operator"))
throw new ExplicitException("Unsupported operator; use 'is successful' or 'is failed'");
throw e;
} Prevention
- 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
When it happens
Trigger: 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).
Common situations: 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.
Related errors
AI-assisted analysis of theonedev/onedev@d44925c47c (2026-09-06).
Data as JSON: /api/errors/2530d8dd2ce690a1.
Report an issue: GitHub.