apache/hadoop · error · IOException

Missing ')'

Error message

Missing ')'

What it means

After tokenizing the whole join expression, Parser.parse (Parser.java:549-564) succeeds only if the stack reduces to exactly one composite node (TType.CIF token). 'Missing \')\'' is the catch-all failure when the residual stack is not a single node — most commonly an unclosed operator call, but also any leftover or malformed tokens.

Source

Thrown at hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/main/java/org/apache/hadoop/mapreduce/lib/join/Parser.java:563

      CompositeInputFormat.JOIN_COMPARATOR, null, WritableComparator.class);
    Lexer lex = new Lexer(expr);
    Stack<Token> st = new Stack<Token>();
    Token tok;
    while ((tok = lex.next()) != null) {
      if (TType.RPAREN.equals(tok.getType())) {
        st.push(reduce(st, conf));
      } else {
        st.push(tok);
      }
    }
    if (st.size() == 1 && TType.CIF.equals(st.peek().getType())) {
      Node ret = st.pop().getNode();
      if (cmpcl != null) {
        ret.setKeyComparator(cmpcl);
      }
      return ret;
    }
    throw new IOException("Missing ')'");
  }

}

View on GitHub (pinned to 2add963021)

Solutions

  1. Close every opened parenthesis — count and match '(' vs ')'
  2. Wrap bare tbl(...) fragments in an operator: inner(tbl(...)) not just tbl(...)
  3. Build the expression with CompositeInputFormat.compose(op, infClass, paths...) which always closes correctly
  4. Validate early: Parser.parse(expr, conf) in a unit test; also assert expr.chars().filter(c -> c=='(').count() == expr.chars().filter(c -> c==')').count() as a cheap smoke check

Example fix

// before
String expr = "inner(tbl(fmt, \"/a\"), tbl(fmt, \"/b\")"; // missing final ')'

// after
String expr = "inner(tbl(fmt, \"/a\"), tbl(fmt, \"/b\"))";
Defensive patterns

Strategy: validation

Validate before calling

static void requireSingleReducibleRoot(String expr) {
  int depth = 0;
  for (char c : expr.toCharArray()) { if (c=='(') depth++; else if (c==')') depth--; }
  if (depth != 0) throw new IllegalArgumentException("Missing ')' in join expression (unclosed operator)");
  if (!expr.matches("^(inner|outer|override)\\(.*\\)$") && !expr.contains("("))
    throw new IllegalArgumentException("Expression must be an operator call wrapping tbl nodes");
}

Try / catch

try { Parser.parse(expr, conf); } catch (IOException e) { if ("Missing ')'".equals(e.getMessage())) throw new IllegalArgumentException("Unclosed or malformed join expression: " + expr, e); throw e; }

Prevention

When it happens

Trigger: Unbalanced expression with a missing closing paren: 'inner(tbl(fmt,"/a"), tbl(fmt,"/b")' — reduce never fires for the outer call, leaving multiple tokens on the stack; extra tokens after a complete expression 'inner(...) tbl(...)'; an expression that reduces to a lone tbl node without an enclosing join operator (stack holds a non-CIF-reducible state per the strict final check).

Common situations: Hand-built mapreduce.join.expr strings with an omitted closer; line concatenation where the final ')' line is dropped; editing long expressions and deleting a paren; forgetting that the outermost call must be a composite operator around tbl nodes.

Related errors


AI-assisted analysis of apache/hadoop@2add963021 (2026-08-22). Data as JSON: /api/errors/96bc19376deb8de6. Report an issue: GitHub.