flowable/flowable-engine · error · ScanException

invalid character '" + c1 + "'

Error message

invalid character '" + c1 + "'

What it means

Scanner.nextEval() throws ScanException("invalid character 'c'") when, while scanning the inside of a ${...} evaluation block, the current character cannot start any valid expression token (not a digit, letter, quote, operator, or whitespace). The expected field is 'expression token'.

Solutions

  1. Find and remove/replace the reported invalid character (ScanException includes position and the character).
  2. Re-type the expression manually instead of pasting to eliminate smart quotes and invisible Unicode.
  3. Ensure only supported operator/symbol characters are used inside ${...} blocks.
  4. Check file encoding (UTF-8 vs Latin-1) if the character came from a config file.

Example fix

// before
String expr = "${order#total > 5}"; // '#' invalid here
// after
String expr = "${order.total > 5}";
Defensive patterns

Strategy: validation

Validate before calling

// reject characters that cannot start an EL token inside ${...}
Pattern VALID = Pattern.compile("[A-Za-z0-9_$'\"().,=<>!+\-*/%?:&|^~{}\\[\\] ]*");
boolean scanClean(String expr) { return VALID.matcher(expr).matches(); }

Try / catch

try {
    builder.build(expr);
} catch (TreeBuilderException e) {
    throw new IllegalArgumentException("Invalid character in expression near pos " + e.getPosition() + ": " + expr);
}

Prevention

When it happens

Trigger: Any character outside the EL grammar appearing inside ${...}, such as '@', '#', '!', misused, a stray '%' or '~' that is not part of an operator, or a control/unicode character pasted into the expression.

Common situations: Copy-pasting expressions from documents/emails introducing smart quotes or invisible characters; typos in operators (e.g. '##'); placeholder syntax from other templating engines (#{} or %%{}) pasted into a ${} block; corrupted config files with encoding issues.

Understand the failure class

Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.

Related errors


AI-assisted analysis of flowable/flowable-engine@d6d39ce1c6 (2026-09-11). Data as JSON: /api/errors/3a788e9679adb3d3. Report an issue: GitHub.

Appendix: source

Thrown at modules/flowable-engine-common/src/main/java/org/flowable/common/engine/impl/de/odysseus/el/tree/impl/Scanner.java:451

		if (Character.isJavaIdentifierStart(c1)) {
			int i = position+1;
			int l = input.length();
			while (i < l && Character.isJavaIdentifierPart(input.charAt(i))) {
				i++;
			}
			String name = input.substring(position, i);
			Token keyword;
			switch (token.getSymbol()) {
				case COLON:
					keyword = javaKeyword(name);
					break;
				default:
					keyword = keyword(name);
			}
			return keyword == null ? token(Symbol.IDENTIFIER, name, i - position) : keyword;
		}

		throw new ScanException(position, "invalid character '" + c1 + "'", "expression token");
	}
	
	protected Token nextToken() throws ScanException {
		if (isEval()) {
			if (input.charAt(position) == '}') {
				return fixed(Symbol.END_EVAL);
			}
			return nextEval();
		} else {
			if (position+1 < input.length() && input.charAt(position+1) == '{') {
				switch (input.charAt(position)) {
					case '#':
						return fixed(Symbol.START_EVAL_DEFERRED);
					case '$':
						return fixed(Symbol.START_EVAL_DYNAMIC);
				}
			}
			return nextText();

View on GitHub (pinned to d6d39ce1c6)