hibernate/hibernate-orm · error · IllegalArgumentException
unmatched placeholder start [${property}]
Error message
unmatched placeholder start [${property}] What it means
ConfigurationHelper.resolvePlaceHolder interpolates ${...} markers inside property values against System.getProperty during Hibernate configuration resolution. When a value opens a placeholder with '${' but the scan reaches the end of the string without finding the matching '}', this IllegalArgumentException is thrown, echoing the whole malformed property value. Unresolvable-but-well-formed placeholders are NOT an error (they are replaced with an empty string); only the unterminated form throws.
Source
Thrown at hibernate-core/src/main/java/org/hibernate/internal/util/config/ConfigurationHelper.java:381
public static String resolvePlaceHolder(String property) {
if ( !property.contains( PLACEHOLDER_START ) ) {
return property;
}
final var result = new StringBuilder();
final char[] chars = property.toCharArray();
for ( int pos = 0; pos < chars.length; pos++ ) {
if ( chars[pos] == '$' ) {
// peek ahead
if ( chars[pos+1] == '{' ) {
// we have a placeholder, spin forward till we find the end
final var systemPropertyName = new StringBuilder();
int x = pos + 2;
for ( ; x < chars.length && chars[x] != '}'; x++ ) {
systemPropertyName.append( chars[x] );
// if we reach the end of the string w/o finding the
// matching end, that is an exception
if ( x == chars.length - 1 ) {
throw new IllegalArgumentException( "unmatched placeholder start [" + property + "]" );
}
}
final String systemProperty = extractFromSystem( systemPropertyName.toString() );
result.append( systemProperty == null ? "" : systemProperty );
pos = x + 1;
// make sure spinning forward did not put us past the end of the buffer...
if ( pos >= chars.length ) {
break;
}
}
}
result.append( chars[pos] );
}
return result.isEmpty() ? null : result.toString();
}
private static String extractFromSystem(String systemPropertyName) {
try {View on GitHub (pinned to fad1729dce)
Solutions
- Add the missing '}' so every '${' has a matching close: jdbc:postgresql://${DB_HOST}/mydb
- Verify the property resolves from System properties (System.getProperty) - that is the only source consulted
- If the '${' is literal data (password, SQL), replace or escape it so it is not treated as a placeholder
Example fix
# before
hibernate.connection.url=jdbc:postgresql://${DB_HOST
# after
hibernate.connection.url=jdbc:postgresql://${DB_HOST}/mydb Defensive patterns
Strategy: validation
Validate before calling
static boolean hasBalancedPlaceholders(String value) {
int open = value.indexOf("${");
while (open >= 0) {
if (value.indexOf('}', open + 2) < 0) return false;
open = value.indexOf("${", open + 2);
}
return true;
}
// gate config load
properties.forEach((k, v) -> {
if (!hasBalancedPlaceholders(v)) throw new IllegalStateException("Unterminated '${' in " + k + "=" + v);
}); Try / catch
try {
sessionFactory = new Configuration().mergeProperties(props).buildSessionFactory();
} catch (IllegalArgumentException e) {
if (e.getMessage().contains("unmatched placeholder start")) {
throw new IllegalStateException("Fix the unterminated ${...} in hibernate properties: " + e.getMessage(), e);
}
throw e;
} Prevention
- Lint config files for '${' without a matching '}' before deploy
- Remember Hibernate resolves placeholders from JVM system properties only - set them with -D or System.setProperty
- Escape or avoid literal '${' sequences in passwords/SQL values
When it happens
Trigger: Any hibernate.* property whose value contains an unterminated placeholder, e.g. hibernate.connection.url=jdbc:postgresql://${DB_HOST (missing '}'); or a literal '${' sequence inside a password, SQL fragment, or template residue like ${artifactId} left in a resource-filtered config file.
Common situations: Typos that delete the closing brace; copy-pasting Spring-style ${VAR} placeholders (note: Hibernate resolves them from JVM system properties only, and does not support default-value syntax); Maven/Gradle resource filtering leaving unclosed placeholders in hibernate.properties/persistence.xml.
Related errors
- Unknown placeholder {token}
- The {storageEngine} storage engine is not supported
- Unknown Cache Mode: " + setting
- Unrecognized graph_parser_mode value : " + graphParserMode +
- Audit graph mutation plan used with non-graph action queue
AI-assisted analysis of hibernate/hibernate-orm@fad1729dce (2026-08-22).
Data as JSON: /api/errors/8881631e10d354bc.
Report an issue: GitHub.