apache/hadoop · error · IOException
Unmatched ')'
Error message
Unmatched ')'
What it means
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.
Source
Thrown at hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/main/java/org/apache/hadoop/mapreduce/lib/join/Parser.java:525
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append(ident + "(");
for (Node n : kids) {
sb.append(n.toString() + ",");
}
sb.setCharAt(sb.length() - 1, ')');
return sb.toString();
}
}
private static Token reduce(Stack<Token> st, Configuration conf)
throws IOException {
LinkedList<Token> args = new LinkedList<Token>();
while (!st.isEmpty() && !TType.LPAREN.equals(st.peek().getType())) {
args.addFirst(st.pop());
}
if (st.isEmpty()) {
throw new IOException("Unmatched ')'");
}
st.pop();
if (st.isEmpty() || !TType.IDENT.equals(st.peek().getType())) {
throw new IOException("Identifier expected");
}
Node n = Node.forIdent(st.pop().getStr());
n.parse(args, conf);
return new NodeToken(n);
}
/**
* Given an expression and an optional comparator, build a tree of
* InputFormats using the comparator to sort keys.
*/
static Node parse(String expr, Configuration conf) throws IOException {
if (null == expr) {
throw new IOException("Expression is null");
}View on GitHub (pinned to 2add963021)
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...)
Example fix
// before String expr = "inner(tbl(fmt, \"/a\"), tbl(fmt, \"/b\")))"; // after String expr = "inner(tbl(fmt, \"/a\"), tbl(fmt, \"/b\"))";
Defensive patterns
Strategy: validation
Validate before calling
static void requireBalancedParens(String expr) {
int depth = 0;
for (char c : expr.toCharArray()) {
if (c == '(') depth++;
else if (c == ')') { depth--; if (depth < 0) throw new IllegalArgumentException("Unmatched ')' in join expression"); }
}
if (depth != 0) throw new IllegalArgumentException("Unbalanced parentheses in join expression");
} Try / catch
try { Parser.parse(expr, conf); } catch (IOException e) { throw new IllegalArgumentException("Unbalanced join expression: " + expr, e); } Prevention
- Run a paren-balance check before setting mapreduce.join.expr
- Generate expressions programmatically with compose()
- Code-review any hand-edited multi-line expression concatenation
When it happens
Trigger: 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.
Common situations: 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.
Related errors
AI-assisted analysis of apache/hadoop@2add963021 (2026-08-22).
Data as JSON: /api/errors/b99124549b5c2089.
Report an issue: GitHub.