pinpoint-apm/pinpoint · error · OracleConnectionStringException
Syntax error. expected token:'(' or ')' :<token>
Error message
Syntax error. expected token:'(' or ')' :<token> What it means
Inside parseKeyValue(), after a key has already been reduced (nonTerminalValue is true — a nested child key-value was parsed), a LITERAL token appears where only '(' or ')' is grammatically valid, so the parser throws. It means two sibling values or a value after a nested block without an '=' — structurally ambiguous input.
Source
Thrown at agent-module/plugins/oracle-jdbc/src/main/java/com/navercorp/pinpoint/plugin/jdbc/oracle/parser/OracleNetConnectionDescriptorParser.java:125
final Token token = this.tokenizer.lookAheadToken();
if (token == null) {
// Abnormal termination.
throw new OracleConnectionStringException("Syntax error. lookAheadToken is null");
}
if (token.getType() == OracleNetConnectionDescriptorTokenizer.TYPE_KEY_START) {
nonTerminalValue = true;
KeyValue child = parseKeyValue();
keyValue.addKeyValueList(child);
// if next token is ')', value is completed.
Token endCheck = this.tokenizer.lookAheadToken();
if (endCheck == OracleNetConnectionDescriptorTokenizer.TOKEN_KEY_END_OBJECT) {
this.tokenizer.nextPosition();
return keyValue.build();
}
} else if(token.getType() == OracleNetConnectionDescriptorTokenizer.TYPE_LITERAL) {
if (nonTerminalValue) {
throw new OracleConnectionStringException("Syntax error. expected token:'(' or ')' :" + token.getToken());
}
// We already have checked current token by lookAheadToken(). Proceed to next token.
this.tokenizer.nextPosition();
keyValue.setValue(token.getToken());
this.tokenizer.checkEndToken();
return keyValue.build();
} else if(token.getType() == OracleNetConnectionDescriptorTokenizer.TYPE_KEY_END){
this.tokenizer.nextPosition();
// This could happen if value is empty.
// Does it allow empty value?
return keyValue.build();
} else {
// Cannot reach here because we checked all those possible cases, START, END and LITERAL.
// Adding new token type could cause error.
// In case of syntax error, EOF can come to here.
throw new OracleConnectionStringException("Syntax error. " + token.getToken());
}View on GitHub (pinned to 744c3d3075)
Solutions
- Rewrite the offending segment so every attribute is a parenthesized (NAME=value) pair
- Check for a missing '=' between key and value and add it
- Remove the stray literal token that follows the nested child block
- Validate the descriptor with a tnsnames parser or try connecting with plain sqlplus using the same descriptor to confirm syntax
Example fix
// before String url = "jdbc:oracle:thin:@(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=db)(PORT=1521))(CONNECT_DATA=(SERVICE_NAME=svc)SID=x))"; // after String url = "jdbc:oracle:thin:@(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=db)(PORT=1521))(CONNECT_DATA=(SERVICE_NAME=svc)(SID=x)))";
Defensive patterns
Strategy: validation
Validate before calling
// every attribute must be a parenthesized (KEY=value) pair
static boolean onlyParenthesizedPairs(String descriptor) {
java.util.Deque<Character> st = new java.util.ArrayDeque<>();
for (char c : descriptor.toCharArray()) {
if (c == '(') st.push(c);
else if (c == ')') { if (st.isEmpty()) return false; st.pop(); }
}
return st.isEmpty();
} Try / catch
try {
parser.parse(url);
} catch (OracleConnectionStringException e) {
log.error("Oracle descriptor has a stray literal where '(' or ')' expected: {}", url, e);
throw new ConfigurationException("Descriptor syntax: use (KEY=value) pairs", e);
} Prevention
- Ensure each key is wrapped as (KEY=value) with '=' between name and value
- Do not place bare tokens after a nested child block
- Test the descriptor with sqlplus before deploying it in the JDBC URL
When it happens
Trigger: A descriptor like (CONNECT_DATA=(SERVICE_NAME=svc)extra) or (KEY=a=b): after parsing the nested child or the first literal, a stray literal token arrives and hits the nonTerminalValue branch.
Common situations: Hand-edited descriptors with duplicated values; missing '=' between key and value so the key text is treated as a literal after a nested block; typos like (HOST=db PORT=1521) missing the inner parentheses and '='.
Understand the failure class
Background: "Invalid URL" errors: why new URL(), URI.parse, and reqwest::Url reject your string — missing scheme, whitespace, and bad path format — this error's family across 39 libraries.
Related errors
- parsing error. expected token:'EOF' token:null
- syntax error. Expected token='(' :<token>
- description node not found
- description_list node not found
- invalid oracle jdbc url. expected token:(thin or oci) url:<u
AI-assisted analysis of pinpoint-apm/pinpoint@744c3d3075 (2026-09-07).
Data as JSON: /api/errors/9b762436da806b21.
Report an issue: GitHub.