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
- Close every opened parenthesis — count and match '(' vs ')'
- Wrap bare tbl(...) fragments in an operator: inner(tbl(...)) not just tbl(...)
- Build the expression with CompositeInputFormat.compose(op, infClass, paths...) which always closes correctly
- 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
- Close every operator call; count '(' vs ')'
- Ensure the outermost element is a composite operator wrapping tbl nodes
- Use compose() which always emits balanced output
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.