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

  1. Restrict the metric query to supported operators: 'status is successful' / 'status is failed' (BuildIsSuccessful / BuildIsFailed).
  2. Check the OneDev version: if the grammar added operators, upgrade the code so visitOperatorCriteria maps them to criteria classes.
  3. Catch ExplicitException in the query UI/API layer and show the list of valid operators to the user.
  4. 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

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.