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

  1. Rewrite the offending segment so every attribute is a parenthesized (NAME=value) pair
  2. Check for a missing '=' between key and value and add it
  3. Remove the stray literal token that follows the nested child block
  4. 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

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


AI-assisted analysis of pinpoint-apm/pinpoint@744c3d3075 (2026-09-07). Data as JSON: /api/errors/9b762436da806b21. Report an issue: GitHub.