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

  1. Audit every row construction site so each String[] has the same number of elements as the first row
  2. Normalize rows before the call: pad short rows with empty strings or truncate/merge long rows
  3. 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

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)