frohoff/ysoserial · error · IllegalStateException
mismatched columns
Error message
mismatched columns
What it means
Strings.formatTable builds a column-width table by iterating rows and asserting every row has the same number of columns as the first row. When a row's length differs from rows.get(0).length, it throws IllegalStateException("mismatched columns") because a rectangular table cannot be formatted from ragged input.
Solutions
- Audit every row construction site so each String[] has the same number of elements as the first row
- Normalize rows before the call: pad short rows with empty strings or truncate/merge long rows
- Add a unit test asserting all rows share the same length before calling formatTable
Example fix
// before
List<String[]> rows = new ArrayList<>();
rows.add(new String[]{"name", "target"});
rows.add(new String[]{"CommonsCollections1"}); // 1 column
Strings.formatTable(rows);
// after
rows.add(new String[]{"CommonsCollections1", ""}); // same column count
Strings.formatTable(rows); Defensive patterns
Strategy: validation
Validate before calling
if (rows.isEmpty()) throw new IllegalArgumentException("no rows");
int cols = rows.get(0).length;
for (String[] row : rows) {
if (row.length != cols) throw new IllegalArgumentException("row has " + row.length + " cols, expected " + cols);
} Type guard
static boolean isRectangular(List<String[]> rows) {
return rows != null && !rows.isEmpty() &&
rows.stream().allMatch(r -> r.length == rows.get(0).length);
} Try / catch
try {
Strings.formatTable(rows);
} catch (IllegalStateException e) {
// log offending row lengths and fall back to plain printing
} Prevention
- Build rows with a shared helper/factory so column count cannot drift
- Add a unit test asserting all rows have equal length
- Prefer a small row record/class over raw String[] where possible
When it happens
Trigger: Calling ysoserial.Strings.formatTable(List<String[]>) with a list whose String[] rows have inconsistent lengths — e.g. the first row has 3 elements but a later row has 2 or 4. Note an empty list throws IndexOutOfBoundsException at rows.get(0) instead.
Common situations: Building CLI/help output tables where rows are assembled from variable-length data (e.g. payload name + optional description columns), or a refactor adds a field to some row-construction sites but not others.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
AI-assisted analysis of frohoff/ysoserial@218bcffcaa (2026-09-12).
Data as JSON: /api/errors/e0cd2f6d015a8353.
Report an issue: GitHub.
Appendix: source
Thrown at src/main/java/ysoserial/Strings.java:33
if (! first) sb.append(sep);
if (prefix != null) sb.append(prefix);
sb.append(s);
if (suffix != null) sb.append(suffix);
first = false;
}
return sb.toString();
}
public static String repeat(String str, int num) {
final String[] strs = new String[num];
Arrays.fill(strs, str);
return join(Arrays.asList(strs), "", "", "");
}
public static List<String> formatTable(List<String[]> rows) {
final Integer[] maxLengths = new Integer[rows.get(0).length];
for (String[] row : rows) {
if (maxLengths.length != row.length) throw new IllegalStateException("mismatched columns");
for (int i = 0; i < maxLengths.length; i++) {
if (maxLengths[i] == null || maxLengths[i] < row[i].length()) {
maxLengths[i] = row[i].length();
}
}
}
final List<String> lines = new LinkedList<String>();
for (String[] row : rows) {
for (int i = 0; i < maxLengths.length; i++) {
final String pad = repeat(" ", maxLengths[i] - row[i].length());
row[i] = row[i] + pad;
}
lines.add(join(Arrays.asList(row), " ", "", ""));
}
return lines;
}
View on GitHub (pinned to 218bcffcaa)