{"record":{"id":"b99124549b5c2089","repo":"apache/hadoop","slug":"unmatched-b99124","errorCode":null,"errorMessage":"Unmatched ')'","messagePattern":"Unmatched '\\)'","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":525,"sourceCode":"    public String toString() {\n      StringBuilder sb = new StringBuilder();\n      sb.append(ident + \"(\");\n      for (Node n : kids) {\n        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    }","sourceCodeStart":507,"sourceCodeEnd":543,"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#L507-L543","documentation":"Parser.reduce (Parser.java:525) processes a ')' by popping tokens off the parse stack until it finds the matching '('. If the stack empties first, the ')' has no opener and 'Unmatched \\')\\'' is thrown — the join expression has more closing parentheses than opening ones.","triggerScenarios":"Any expression where a ')' appears before its '(': 'inner(tbl(fmt,\"/a\")))' (extra trailing paren), 'tbl(fmt,\"/a\"))(...)', or a copy-paste duplication of a closing fragment. During parse (Parser.java:557-561) each RPAREN token triggers reduce(), which throws when the stack is empty.","commonSituations":"Hand-editing mapreduce.join.expr and adding a stray ')'; string concatenation bugs that append an extra closer; copying multi-line expressions and duplicating the last line's parenthesis.","solutions":["Balance the parentheses: count '(' and ')' in the expression and make them equal and properly nested","Restructure using CompositeInputFormat.compose(...) so parentheses are generated correctly","Unit-test with Parser.parse(expr, conf) before submitting the job","Lint hand-written expressions in code review — the grammar is tiny; each operator call is ident(args...)"],"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 requireBalancedParens(String expr) {\n  int depth = 0;\n  for (char c : expr.toCharArray()) {\n    if (c == '(') depth++;\n    else if (c == ')') { depth--; if (depth < 0) throw new IllegalArgumentException(\"Unmatched ')' in join expression\"); }\n  }\n  if (depth != 0) throw new IllegalArgumentException(\"Unbalanced parentheses in join expression\");\n}","typeGuard":null,"tryCatchPattern":"try { Parser.parse(expr, conf); } catch (IOException e) { throw new IllegalArgumentException(\"Unbalanced join expression: \" + expr, e); }","preventionTips":["Run a paren-balance check before setting mapreduce.join.expr","Generate expressions programmatically with compose()","Code-review any hand-edited multi-line expression concatenation"],"tags":["hadoop","mapreduce","join","parser","parentheses"],"backgroundTag":"unbalanced-parentheses","analyzedSha":"2add9630210752f88ceb1bb74eb65e37bf41da8e","analyzedAt":"2026-08-22T19:55:07.957Z","schemaVersion":2},"datasetVersion":"2026-08-22T20:17:22.307Z"}