Skip to content
sheetsmith

Validation and errors

A SheetsmithConfigurationException lists every error found, one per line, in this format:

[code] package.Class$Nested.element: message
Part Meaning
[code] The violated rule, V-01 to V-20. Look it up in section 11.3.
package.Class The fully qualified name of the class involved: it tells which source file to open. It is the sheet class, or the style sheet that declares the faulty style.
$Nested Present when the class is nested inside another class: com.example.Reports$InvoiceRow is the class InvoiceRow declared inside Reports.java.
element What is wrong inside the class: a field name (amount), @ExcelSheet (the class-level annotation), @ExcelStyle(name) (a named style), @ExcelStyle(#n) (the n-th style declared on the class, when its name is blank), or sheets[i] (the i-th element, 0-based, of the list passed to generate, for input errors). It is omitted when the error concerns the class or the input as a whole.
message The problem, often with the attribute or slot involved, for example (referenced by body.lastRow).

The class and the separating dot are omitted for input errors, and the colon is omitted when there is neither class nor element. Source line numbers are not available: annotations do not carry them at runtime.

Example of a complete message, as it appears in a log:

cloud.baldilorenzo.sheetsmith.SheetsmithConfigurationException: [V-06] com.example.export.InvoiceLine.amount: style 'money' not found (referenced by styles.base)
[V-15] com.example.export.InvoiceLine.@ExcelSheet: titleStyle is set but title is empty
[V-13] com.example.export.CorporateStyles.@ExcelStyle(header): fillColor '#1F4E7' is not a valid colour: expected #RRGGBB or the name of an IndexedColors constant
[V-19] sheets[1]: sheet name 'Q3/2026' must not contain any of \ / ? * [ ] :

Reading it: the first line concerns the field amount of InvoiceLine, whose column slot styles.base references a style money that does not exist; the third concerns the style header declared on the style sheet CorporateStyles; the last concerns the second sheet passed to generate.

Programmatic access: exception.errors() returns the ConfigurationError records, with code(), type(), element() and message().

Rules Phase Checked by
V-01 to V-09, V-13 to V-17 Extraction of the metadata of a sheet class generate, validate, startup validation
V-10 to V-12 Binding of a converter to each column generate, validate, startup validation
V-18 to V-20 Input of generate generate only

Validation is fail-fast but complete: every rule is checked and all the errors of the input and of all the sheet classes involved are reported together in one exception. Nothing is written when there is at least one error. Anomalies are never corrected silently.

A SheetsmithGenerationException is thrown while a sheet is written. Its message ends with the location: (sheet 'S', row N, field 'f'), where the row and the field appear only when relevant.

Cause Message Row Field Cause attached
Null element in the data list null element in the data list (sheet 'Orders', row 7) yes no no
Getter, accessor or field read that throws cannot read the value (method getTotal()): java.lang.IllegalStateException: ... (sheet 'Orders', row 7, field 'total') yes yes yes
Converter that throws converter failed: java.lang.IllegalArgumentException: ... (sheet 'Orders', row 7, field 'total') yes yes yes
Converter that returns null converter returned null; return CellValue.blank() for an empty cell (sheet 'Orders', row 7, field 'total') yes yes no
Text longer than 32,767 characters text of 40000 characters exceeds the Excel limit of 32767 (sheet 'Orders', row 7, field 'notes') yes yes no
Too many rows for one sheet the sheet needs 1048580 rows, more than the Excel limit of 1048576 (sheet 'Orders') no (0) no no

The accessor description inside the message is method getX() when a getter was used, or field x when the field was read directly. The row index is the 1-based position in the data list, so row 7 is rows.get(6). The row limit counts the title and the header, and is checked before the sheet is written.

Checked exceptions thrown by a getter without declaration (for example through “sneaky throw” techniques) are wrapped in java.lang.reflect.UndeclaredThrowableException and then reported like any other getter failure. Errors (such as OutOfMemoryError or StackOverflowError) are not wrapped.

Error When Notes
java.io.UncheckedIOException Serialisation of the workbook fails, including a failure of the caller’s stream (closed connection, full disk) Not a sheetsmith exception, on purpose. The original IOException is the cause. With the stream method, partial content may have been written.
NullPointerException A null argument: sheets, an element of sheets, out, type, or a null component of the public records Programming error.
OutOfMemoryError The workbook does not fit in the heap See section 12.3.
Source Exception Message
Builder.converter called twice for the same type IllegalArgumentException a converter is already registered for type X
SheetsmithDefaults with invalid values IllegalArgumentException dateFormat must not be blank, dateTimeFormat must not be blank, preset must not be INHERIT, accentColor 'X' is not a valid colour: ...
Two converter beans for the same type (Spring) IllegalStateException at startup converter beans 'a' and 'b' both handle type X; keep only one of them
Converter bean with an undeterminable type (Spring) IllegalStateException at startup cannot resolve the type handled by converter bean 'x'; ...
Invalid sheetsmith.* property (Spring) binding error or IllegalArgumentException at startup as above
Startup validation failure (Spring) SheetsmithConfigurationException at startup the full list of errors
class SheetClassesTest {
private final Sheetsmith sheetsmith = Sheetsmith.builder()
.converter(Money.class, new MoneyConverter()) // configure like production
.build();
@ParameterizedTest
@ValueSource(classes = {CustomerRow.class, InvoiceLine.class, OrderRow.class})
void isValid(Class<?> sheetClass) {
assertDoesNotThrow(() -> sheetsmith.validate(sheetClass));
}
@Test
void reportsTheExpectedError() {
SheetsmithConfigurationException e = assertThrows(SheetsmithConfigurationException.class,
() -> sheetsmith.validate(BrokenRow.class));
assertThat(e.errors()).extracting(ConfigurationError::code).containsExactly("V-06");
}
}

In a Spring Boot test, inject the Sheetsmith bean to validate with the real converters, or rely on the startup validation, which fails the test context.