{"record":{"id":"da60b122594a176e","repo":"apache/hadoop","slug":"identifier-expected-da60b1","errorCode":null,"errorMessage":"Identifier expected","messagePattern":"Identifier expected","errorType":"exception","errorClass":"IOException","httpStatus":null,"severity":"error","filePath":"hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/main/java/org/apache/hadoop/mapreduce/lib/join/Parser.java","lineNumber":529,"sourceCode":"        sb.append(n.toString() + \",\");\n      }\n      sb.setCharAt(sb.length() - 1, ')');\n      return sb.toString();\n    }\n  }\n\n  private static Token reduce(Stack<Token> st, Configuration conf) \n      throws IOException {\n    LinkedList<Token> args = new LinkedList<Token>();\n    while (!st.isEmpty() && !TType.LPAREN.equals(st.peek().getType())) {\n      args.addFirst(st.pop());\n    }\n    if (st.isEmpty()) {\n      throw new IOException(\"Unmatched ')'\");\n    }\n    st.pop();\n    if (st.isEmpty() || !TType.IDENT.equals(st.peek().getType())) {\n      throw new IOException(\"Identifier expected\");\n    }\n    Node n = Node.forIdent(st.pop().getStr());\n    n.parse(args, conf);\n    return new NodeToken(n);\n  }\n\n  /**\n   * Given an expression and an optional comparator, build a tree of\n   * InputFormats using the comparator to sort keys.\n   */\n  static Node parse(String expr, Configuration conf) throws IOException {\n    if (null == expr) {\n      throw new IOException(\"Expression is null\");\n    }\n    Class<? extends WritableComparator> cmpcl = conf.getClass(\n      CompositeInputFormat.JOIN_COMPARATOR, null, WritableComparator.class);\n    Lexer lex = new Lexer(expr);\n    Stack<Token> st = new Stack<Token>();","sourceCodeStart":511,"sourceCodeEnd":547,"githubUrl":"https://github.com/apache/hadoop/blob/2add9630210752f88ceb1bb74eb65e37bf41da8e/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/main/java/org/apache/hadoop/mapreduce/lib/join/Parser.java#L511-L547","documentation":"Parser.reduce (Parser.java:529) pops the '(' during reduction and then requires the next stack entry to be an IDENT token — the operator name of the join expression (inner, outer, override, tbl, or a registered custom ident). 'Identifier expected' means the '(' had no identifier immediately before it, so the parenthesized group is not a valid function call.","triggerScenarios":"Expressions like '(tbl(fmt,\"/a\"))' (paren before ident), 'inner((tbl(...)))' (double paren after operator), ',(...)' or stray '(' anywhere a node was expected: 'inner(,(tbl(...)))', 'tbl((fmt),\"/a\")'. The grammar treats every parenthesized group as ident(args).","commonSituations":"Hand-writing expressions and adding grouping parens out of habit from general math syntax (the join grammar has no grouping — parens are always call syntax); typos introducing double '((' after an operator; editing tokens around an existing paren.","solutions":["Remove grouping parentheses — every '(' must directly follow an identifier: ident(arg,arg)","Fix double parens after operators: inner((tbl(...))) → inner(tbl(...))","Generate expressions via CompositeInputFormat.compose(...) instead of hand-writing","Validate with Parser.parse(expr, conf) in a test before job submission"],"exampleFix":"// before\nString expr = \"inner((tbl(fmt, \\\"/a\\\")), tbl(fmt, \\\"/b\\\"))\";\n\n// after\nString expr = \"inner(tbl(fmt, \\\"/a\\\"), tbl(fmt, \\\"/b\\\"))\";","handlingStrategy":"validation","validationCode":"static void requireIdentBeforeParen(String expr) {\n  // no '(' may be preceded by '(' or ',' or start of string\n  java.util.regex.Matcher m = java.util.regex.Pattern.compile(\"(^|[(,])\\\\s*\\\\(\").matcher(expr);\n  if (m.find()) throw new IllegalArgumentException(\"'(' must directly follow an identifier (no grouping parens)\");\n}","typeGuard":null,"tryCatchPattern":"try { Parser.parse(expr, conf); } catch (IOException e) { throw new IllegalArgumentException(\"Join grammar error (parens are call syntax, not grouping): \" + expr, e); }","preventionTips":["Remember the grammar: every '(' is a function call and must follow an identifier","Never add grouping parentheses around arguments","Use compose() helpers for generation"],"tags":["hadoop","mapreduce","join","parser","syntax"],"backgroundTag":"expression-parse-error","analyzedSha":"2add9630210752f88ceb1bb74eb65e37bf41da8e","analyzedAt":"2026-08-22T19:55:07.957Z","schemaVersion":2},"datasetVersion":"2026-08-23T01:17:44.959Z"}