testcontainers/testcontainers-java · error · ScriptUtils.ScriptParseException
Unclosed dollar quoted string
Error message
Unclosed dollar quoted string [%s].
What it means
ScriptScanner supports PostgreSQL dollar-quoted strings ($tag$...$tag$). When it matches a dollar-quote opening delimiter but cannot find the matching closing delimiter anywhere later in the script, it throws ScriptUtils.ScriptParseException with this message. The dollar-quoted string in the script is never terminated.
Solutions
- Find the reported delimiter in the script and add its matching closing occurrence.
- Check that open/close tags are identical ($fn$ closes with $fn$, not $$).
- Validate the script directly in psql to confirm it parses before wiring it into tests.
Example fix
// before (script.sql) CREATE FUNCTION f() RETURNS int AS $$ BEGIN RETURN 1; END; // after (script.sql) CREATE FUNCTION f() RETURNS int AS $$ BEGIN RETURN 1; END $$;
Defensive patterns
Strategy: validation
Validate before calling
Pattern p = Pattern.compile("\\$[A-Za-z0-9_]*\\$");
Matcher m = p.matcher(script);
if (m.results().count() % 2 != 0) {
throw new IllegalStateException("Odd number of dollar-quote delimiters in script");
} Try / catch
try {
ScriptUtils.executeDatabaseScript(delegate, path, script);
} catch (ScriptUtils.ScriptParseException e) {
if (e.getMessage().startsWith("Unclosed dollar quoted string")) {
// locate the delimiter named in the message and fix the script
}
throw e;
} Prevention
- Validate PostgreSQL scripts with psql before adding them as init scripts.
- Ensure dollar-quote open/close tags are identical strings.
- Avoid ad-hoc edits that drop the closing delimiter of function bodies.
When it happens
Trigger: Executing a PostgreSQL init script containing $$...$$ or $fn$...$fn$ where the closing delimiter is missing, or a script whose dollar-quote tags don't match between open and close.
Common situations: Truncated copy-paste of PL/pgSQL function bodies; writing $$ in the script where a different quoting tag was opened; scripts generated programmatically that drop the final delimiter.
Understand the failure class
Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.
Related errors
- Missing block comment end delimiter…
- Could not load classpath init script
- Error while executing init script
- Failed to execute database script from resource [" + script…
- Could not create new connection
AI-assisted analysis of testcontainers/testcontainers-java@8e549514e3 (2026-09-12).
Data as JSON: /api/errors/d9844bd20007d6c2.
Report an issue: GitHub.
Appendix: source
Thrown at modules/database-commons/src/main/java/org/testcontainers/ext/ScriptScanner.java:125
} else if (c == '\\') {
escaped = true;
} else if (c == quote) {
currentMatch = script.substring(offset, i + 1);
offset = i + 1;
return true;
}
}
}
return false;
}
private boolean matchesDollarQuotedString() {
//Matches $<tag>$ .... $<tag>$
if (matches(dollarQuotedStringDelimiter)) {
String delimiter = currentMatch;
int end = script.indexOf(delimiter, offset);
if (end < 0) {
throw new ScriptUtils.ScriptParseException(
String.format("Unclosed dollar quoted string [%s].", delimiter),
resource
);
}
end += delimiter.length();
currentMatch = delimiter + script.substring(offset, end);
offset = end;
return true;
}
return false;
}
Lexem next() {
if (offset < script.length()) {
if (matches(separator)) {
return Lexem.SEPARATOR;
} else if (matchesSingleLineComment() || matchesMultilineComment()) {
return Lexem.COMMENT;View on GitHub (pinned to 8e549514e3)