theonedev/onedev · error · RuntimeException

Malformed job match

Error message

Malformed job match

What it means

Thrown by JobMatch.parse when the ANTLR lexer/parser reports a syntax error while parsing a job match expression. Wrapped in a RuntimeException with the underlying RecognitionException attached, meaning the match string does not conform to the JobMatch grammar.

Solutions

  1. Validate the match expression syntax against OneDev's job match grammar
  2. Check for unbalanced parentheses, missing 'or'/'and' operators, and unquoted values
  3. Rewrite the expression incrementally, testing in the UI's expression editor if available
  4. See the server log's nested RecognitionException for the offending token/position

Example fix

// before
JobMatch.parse("branch is main &&", false, false); // trailing operator
// after
JobMatch.parse("branch is main", false, false);
Defensive patterns

Strategy: validation

Validate before calling

// validate expression syntax before use
try {
    JobMatch.parse(expression, false, false);
} catch (RuntimeException e) {
    throw new IllegalArgumentException("Invalid job match expression: " + expression, e);
}

Try / catch

try {
    var match = JobMatch.parse(expr, false, false);
} catch (RuntimeException e) {
    // surface syntax error with offending position from cause
}

Prevention

When it happens

Trigger: Parsing an authorization/condition expression with invalid syntax — unbalanced parentheses, unknown keywords, missing operators, stray characters — via JobMatch.parse(expression, withProjectCriteria, withJobCriteria).

Common situations: Typo in match expression in a secret authorization field or build condition; quoting mistakes around values; copied expressions using unsupported syntax from other CI systems.

Understand the failure class

Related errors


AI-assisted analysis of theonedev/onedev@d44925c47c (2026-09-06). Data as JSON: /api/errors/318386c7b1c15cf9. Report an issue: GitHub.

Appendix: source

Thrown at server-core/src/main/java/io/onedev/server/job/match/JobMatch.java:57

	
	public JobMatch(Criteria<JobMatchContext> criteria) {
		this.criteria = criteria;
	}

	public static String getValue(String token) {
		return StringUtils.unescape(FenceAware.unfence(token));
	}
	
	public static JobMatch parse(String jobMatchString, boolean withProjectCriteria, boolean withJobCriteria) {
		CharStream is = CharStreams.fromString(jobMatchString); 
		JobMatchLexer lexer = new JobMatchLexer(is);
		lexer.removeErrorListeners();
		lexer.addErrorListener(new BaseErrorListener() {

			@Override
			public void syntaxError(Recognizer<?, ?> recognizer, Object offendingSymbol, int line,
					int charPositionInLine, String msg, RecognitionException e) {
				throw new RuntimeException("Malformed job match", e);
			}
			
		});
		CommonTokenStream tokens = new CommonTokenStream(lexer);
		JobMatchParser parser = new JobMatchParser(tokens);
		parser.removeErrorListeners();
		parser.setErrorHandler(new BailErrorStrategy());
		JobMatchParser.JobMatchContext JobMatchContext = parser.jobMatch();

		Criteria<io.onedev.server.job.match.JobMatchContext> criteria = new JobMatchBaseVisitor<Criteria<io.onedev.server.job.match.JobMatchContext>>() {

			@Override
			public Criteria<io.onedev.server.job.match.JobMatchContext> visitParensCriteria(JobMatchParser.ParensCriteriaContext ctx) {
				return visit(ctx.criteria()).withParens(true);
			}

			@Override
			public Criteria<io.onedev.server.job.match.JobMatchContext> visitFieldOperatorValueCriteria(JobMatchParser.FieldOperatorValueCriteriaContext ctx) {

View on GitHub (pinned to d44925c47c)