pinpoint-apm/pinpoint · error · OracleConnectionStringException

parsing error. expected token:'EOF' token:null

Error message

parsing error. expected token:'EOF' token:null

What it means

OracleNetConnectionDescriptorParser.parse() finishes by consuming the remaining tokens and expects the very next token to be the tokenizer's singleton EOF token. When tokenizer.nextToken() returns null (the token stream is exhausted before the parser finishes) checkEof() throws this OracleConnectionStringException. It means the connection descriptor string ended mid-structure, typically from unbalanced parentheses.

Source

Thrown at agent-module/plugins/oracle-jdbc/src/main/java/com/navercorp/pinpoint/plugin/jdbc/oracle/parser/OracleNetConnectionDescriptorParser.java:69

        } else {
            throw new IllegalArgumentException("invalid oracle jdbc url. expected token:(" + THIN + " or " + OCI + ") url:" + url);
        }

        // skip thin string
        this.tokenizer.setPosition(position);

        this.tokenizer.parse();
        KeyValue<?> keyValue = parseKeyValue();

        checkEof();

        return keyValue;
    }

    private void checkEof() {
        Token eof = this.tokenizer.nextToken();
        if (eof == null) {
            throw new OracleConnectionStringException("parsing error. expected token:'EOF' token:null");
        }
        if (eof != OracleNetConnectionDescriptorTokenizer.TOKEN_EOF_OBJECT) {
            throw new OracleConnectionStringException("parsing error. expected token:'EOF' token:" + eof);
        }
    }

    public DriverType getDriverType() {
        return driverType;
    }

    private int nextPosition(String driverUrl) {
        final int thinLength = driverUrl.length();
        if (normalizedUrl.startsWith(":@", thinLength)) {
            return thinLength + 2;
        } else if(normalizedUrl.startsWith("@", thinLength)) {
            return thinLength + 1;
        } else {
            throw new OracleConnectionStringException("invalid oracle jdbc url:" + driverUrl);

View on GitHub (pinned to 744c3d3075)

Solutions

  1. Count the parentheses in the descriptor and add the missing ')' for every unclosed '(' before the end of the URL
  2. Use a complete valid descriptor, e.g. jdbc:oracle:thin:@(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=host)(PORT=1521))(CONNECT_DATA=(SERVICE_NAME=svc)))
  3. If you only need host/port/db, switch to the simple URL form jdbc:oracle:thin:@host:1521:SID to avoid descriptor parsing entirely
  4. Log the full driverUrl at parse failure and compare character-by-character with a known-good descriptor

Example fix

// before
String url = "jdbc:oracle:thin:@(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=db)(PORT=1521))(CONNECT_DATA=(SERVICE_NAME=svc))"; // missing final ')'
// after
String url = "jdbc:oracle:thin:@(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=db)(PORT=1521))(CONNECT_DATA=(SERVICE_NAME=svc)))";
Defensive patterns

Strategy: validation

Validate before calling

static boolean isBalanced(String url) {
    int depth = 0;
    for (char c : url.toCharArray()) {
        if (c == '(') depth++;
        else if (c == ')') depth--;
        if (depth < 0) return false;
    }
    return depth == 0;
}
// call before use: if (!isBalanced(jdbcUrl)) failFast(...);

Type guard

boolean isDescriptorUrl(String url) {
    return url != null && url.startsWith("jdbc:oracle:thin:@(") && url.endsWith(")") && isBalanced(url);
}

Try / catch

try {
    DataSource ds = driver.connect(url, props);
} catch (OracleConnectionStringException e) {
    log.error("Malformed Oracle descriptor URL (check parentheses): {}", url, e);
    throw new ConfigurationException("Invalid jdbc url", e);
}

Prevention

When it happens

Trigger: Parsing a jdbc:oracle:thin:@(DESCRIPTION=... style URL whose token stream ends before parse() reaches its terminal checkEof() call — e.g. a descriptor with an unclosed '(' so parseKeyValue recursion consumes all tokens and nextToken() returns null.

Common situations: Copy-pasting a truncated or hand-edited tnsnames-style descriptor into the JDBC URL; a config value wrapped or split so the closing ')' characters are lost; programmatically building a descriptor with a missing closing paren per nested key.

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/5724f7cc9baf1b25. Report an issue: GitHub.