flowable/flowable-engine · error · ScanException

unterminated string

Error message

unterminated string

What it means

Scanner.nextString() throws ScanException('unterminated string') when a string literal ends with a lone backslash: the input ends immediately after an escape character, so the closing quote is never reached. The 'expected' field indicates the closing quote or another escape character was needed.

Solutions

  1. Ensure the string literal has a closing quote and does not end with a dangling backslash.
  2. Double the backslash ('\\') when a literal backslash is intended before the end of the string.
  3. Log the ScanException position to find the exact spot in the expression.

Example fix

// before
String expr = "${'C:\\path\\'"; // ends with lone backslash
// after
String expr = "${'C:\\path\\user'}";
Defensive patterns

Strategy: validation

Validate before calling

boolean endsWithDanglingBackslash(String expr) { return expr.endsWith("\\"); }
// reject before building

Try / catch

try {
    builder.build(expr);
} catch (TreeBuilderException e) {
    if ("unterminated string".equals(e.getMessage())) {
        throw new IllegalArgumentException("Dangling backslash or missing quote in: " + expr);
    }
    throw e;
}

Prevention

When it happens

Trigger: An EL string literal where the input terminates right after a backslash, e.g. "${'abc\\'" (backslash is the last character of the input), encountered while scanning inside a quoted string.

Common situations: Expressions generated by templates or shell scripts where trailing backslashes escape quotes incorrectly; Windows-style paths written as '${'C:\\users\\' }' with an odd number of backslashes; manual edits that dropped the closing quote.

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/1ca8238397a0e1c6. 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:311

		if (escaped) {
			builder.append('\\');
		}
		return token(Symbol.TEXT, builder.toString(), i - position);
	}
	
	/**
	 * string token
	 */
	protected Token nextString() throws ScanException {
		builder.setLength(0);
		char quote = input.charAt(position);
		int i = position+1;
		int l = input.length();
		while (i < l) {
			char c = input.charAt(i++);
			if (c == '\\') {
				if (i == l) {
					throw new ScanException(position, "unterminated string", quote + " or \\");
				} else {
					c = input.charAt(i++);
					if (c == '\\' || c == '\'' || c == '\"') {
						builder.append(c);
					} else {
						throw new ScanException(position, "invalid escape sequence \\" + c, "\\', \\\" or \\\\");
					}
				}
			} else if (c == quote) {
				return token(Symbol.STRING, builder.toString(), i - position);
			} else {
				builder.append(c);
			}
		}
		throw new ScanException(position, "unterminated string", String.valueOf(quote));
	}
	
	/**

View on GitHub (pinned to d6d39ce1c6)