{"record":{"id":"f2cf0c7ce6ab6f9d","repo":"pinpoint-apm/pinpoint","slug":"syntax-error-lookaheadtoken-is-null","errorCode":null,"errorMessage":"Syntax error. lookAheadToken is null","messagePattern":"Syntax error\\. lookAheadToken is null","errorType":"validation","errorClass":"OracleConnectionStringException","httpStatus":null,"severity":"error","filePath":"agent-module/plugins/oracle-jdbc/src/main/java/com/navercorp/pinpoint/plugin/jdbc/oracle/parser/OracleNetConnectionDescriptorParser.java","lineNumber":110,"sourceCode":"\n        // start\n        this.tokenizer.checkStartToken();\n\n\n        // key\n        Token literalToken = this.tokenizer.getLiteralToken();\n        KeyValue.Builder keyValue = new KeyValue.Builder(literalToken.getToken());\n\n        // =\n        this.tokenizer.checkEqualToken();\n\n        // value compare reduce\n        boolean nonTerminalValue = false;\n        while(true) {\n            final Token token = this.tokenizer.lookAheadToken();\n            if (token == null) {\n                // Abnormal termination.\n                throw new OracleConnectionStringException(\"Syntax error. lookAheadToken is null\");\n            }\n            if (token.getType() == OracleNetConnectionDescriptorTokenizer.TYPE_KEY_START) {\n                nonTerminalValue = true;\n                KeyValue child = parseKeyValue();\n                keyValue.addKeyValueList(child);\n\n                // if next token is ')', value is completed.\n                Token endCheck = this.tokenizer.lookAheadToken();\n                if (endCheck == OracleNetConnectionDescriptorTokenizer.TOKEN_KEY_END_OBJECT) {\n                    this.tokenizer.nextPosition();\n                    return keyValue.build();\n                }\n            } else if(token.getType() == OracleNetConnectionDescriptorTokenizer.TYPE_LITERAL) {\n                if (nonTerminalValue) {\n                    throw new OracleConnectionStringException(\"Syntax error. expected token:'(' or ')' :\" + token.getToken());\n                }\n                // We already have checked current token by lookAheadToken(). Proceed to next token.\n                this.tokenizer.nextPosition();","sourceCodeStart":92,"sourceCodeEnd":128,"githubUrl":"https://github.com/pinpoint-apm/pinpoint/blob/744c3d3075e595656abb1ae331ad2c0e4c9eb996/agent-module/plugins/oracle-jdbc/src/main/java/com/navercorp/pinpoint/plugin/jdbc/oracle/parser/OracleNetConnectionDescriptorParser.java#L92-L128","documentation":"parseKeyValue() loops on tokenizer.lookAheadToken(); when it returns null the token stream ran out before a value, key start '(' or key end ')' was seen, so the recursive-descent parser hit abnormal termination and throws. This is a truncated descriptor: the grammar expects more tokens than the input provides.","triggerScenarios":"A descriptor where a key's value section is missing entirely, e.g. '(KEY=)' followed by end-of-input, or '(CONNECT_DATA' ending the string — lookAheadToken() exhausts the stream inside the while(true) loop.","commonSituations":"Copy-paste truncation of a long tnsnames entry; config templating that dropped everything after a variable; an editor or YAML parser stripping the trailing ')' characters or quoting the value away.","solutions":["Restore the missing value/closing structure so every (KEY=VALUE) pair is complete and balanced","Compare the URL against the original tnsnames.ora entry and re-copy it intact","Enable quoting of the descriptor in config files (YAML/single quotes) so special characters aren't stripped","Add a startup validation step that calls the parser on the configured URL and fails fast with the full string logged"],"exampleFix":"// before\nString url = \"jdbc:oracle:thin:@(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=db)(PORT=1521))(CONNECT_DATA\"; // truncated\n// after\nString url = \"jdbc:oracle:thin:@(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=db)(PORT=1521))(CONNECT_DATA=(SERVICE_NAME=svc)))\";","handlingStrategy":"validation","validationCode":"static boolean isCompleteDescriptor(String url) {\n    if (url == null) return false;\n    String d = url.substring(url.indexOf('@') + 1);\n    return d.startsWith(\"(\") && d.endsWith(\")\") && isBalanced(d);\n}","typeGuard":null,"tryCatchPattern":"try {\n    parser.parse(url);\n} catch (OracleConnectionStringException e) {\n    log.error(\"Oracle descriptor truncated (missing value or ')'): {}\", url, e);\n    throw new ConfigurationException(\"Incomplete oracle connection descriptor\", e);\n}","preventionTips":["Never split descriptor URLs across config lines or variables","Quote the full descriptor in YAML/properties so no characters are stripped","Re-copy long descriptors from tnsnames.ora instead of hand-trimming"],"tags":["jdbc","oracle","url-parsing","truncation"],"backgroundTag":"invalid-url-format","analyzedSha":"744c3d3075e595656abb1ae331ad2c0e4c9eb996","analyzedAt":"2026-09-07T18:48:45.289Z","contentChangedAt":"2026-09-07T18:48:45.289Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}