{"record":{"id":"f1ea39062b6186db","repo":"apache/hadoop","slug":"missing","errorCode":null,"errorMessage":"Missing ')'","messagePattern":"Missing '\\)'","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/mapred/join/Parser.java","lineNumber":500,"sourceCode":"      job.getClass(\"mapred.join.keycomparator\", null, WritableComparator.class);\n    Lexer lex = new Lexer(expr);\n    Stack<Token> st = new Stack<Token>();\n    Token tok;\n    while ((tok = lex.next()) != null) {\n      if (TType.RPAREN.equals(tok.getType())) {\n        st.push(reduce(st, job));\n      } else {\n        st.push(tok);\n      }\n    }\n    if (st.size() == 1 && TType.CIF.equals(st.peek().getType())) {\n      Node ret = st.pop().getNode();\n      if (cmpcl != null) {\n        ret.setKeyComparator(cmpcl);\n      }\n      return ret;\n    }\n    throw new IOException(\"Missing ')'\");\n  }\n\n}\n","sourceCodeStart":482,"sourceCodeEnd":504,"githubUrl":"https://github.com/apache/hadoop/blob/2add9630210752f88ceb1bb74eb65e37bf41da8e/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/main/java/org/apache/hadoop/mapred/join/Parser.java#L482-L504","documentation":"After lexing, the whole expression must reduce to exactly one node token (TType.CIF) left on the stack. Anything else ends with IOException(\"Missing ')'\"), the catch-all for an unbalanced or over-specified expression. Despite the message, the most common cause is an unclosed function call — e.g. inner(tbl(\"a\"),tbl(\"b\") — but trailing unconsumed tokens (two top-level calls, stray identifiers) produce it too.","triggerScenarios":"inner(tbl(\"a\"),tbl(\"b\") — missing final ')'; two top-level calls concatenated like inner(...) outer(...); leftover tokens after a complete expression.","commonSituations":"Hand-balanced parentheses in long nested expressions; concatenating two complete expressions into one property; truncation of long expressions in XML config or shell variables.","solutions":["Close every '(' with exactly one ')'","Keep exactly ONE top-level function call spanning all sources, nesting the rest as its arguments","Generate the expression with CompositeInputFormat.compose() and dry-run setFormat(job) client-side"],"exampleFix":"// before\njob.set(\"mapred.join.expr\", \"inner(tbl(F,/a),tbl(F,/b)\");\n\n// after\njob.set(\"mapred.join.expr\", \"inner(tbl(F,/a),tbl(F,/b))\");","handlingStrategy":"validation","validationCode":"static void checkBalanced(String expr) {\n  int depth = 0;\n  boolean inQuote = false;\n  for (char c : expr.toCharArray()) {\n    if (c == '\"') inQuote = !inQuote;\n    else if (!inQuote && c == '(') depth++;\n    else if (!inQuote && c == ')') depth--;\n  }\n  if (depth != 0) throw new IllegalArgumentException(\"unbalanced parentheses in join expression\");\n}\ncheckBalanced(expr);\njob.set(\"mapred.join.expr\", expr);","typeGuard":null,"tryCatchPattern":"try {\n  new CompositeInputFormat<Object>().setFormat(job);\n} catch (IOException e) {\n  throw new IllegalArgumentException(\n      \"expression must reduce to one top-level call; check for a missing ')' \"\n      + \"or trailing tokens\", e);\n}","preventionTips":["Keep exactly one top-level function call; nest everything else as arguments","Close every '(' — count them when hand-editing","Dry-run setFormat(job) client-side for every generated expression"],"tags":["hadoop","mapreduce","join","parser","parentheses","syntax"],"backgroundTag":"unbalanced-parentheses","analyzedSha":"2add9630210752f88ceb1bb74eb65e37bf41da8e","analyzedAt":"2026-08-22T19:55:07.957Z","schemaVersion":2},"datasetVersion":"2026-08-22T20:17:22.307Z"}