Skip to content

RecordFieldSetMapper rejects null values for nullable types after jSpecify nullability refinement #5392

Description

@ahmad-abed-alhamid

Bug description

After the jSpecify nullability refinement in commit 62c4cbf, RecordFieldSetMapper.mapFieldSet() rejects null values for types where null is a valid value (e.g. Long, Integer, BigDecimal, LocalDate).

When reading a CSV where a field is empty or blank, the SimpleTypeConverter correctly converts it to null. However, the Assert.notNull that was added in the commit above rejects this value, causing an IllegalArgumentException.

In Spring Batch 5, null was accepted and the record was created successfully with the field set to null.

Environment

  • Spring Batch: 6.0.3
  • Spring Boot: 4.0.5
  • Java: 21

Minimal Complete Verifiable Example

Based on the spring-batch-mcve template:

Record:

public record MyRecord(String name, Long value) {}

Test:

@Test
void recordFieldSetMapperShouldAcceptNullForNullableTypes() {
    RecordFieldSetMapper<MyRecord> mapper = new RecordFieldSetMapper<>(MyRecord.class);

    DefaultFieldSet fieldSet = new DefaultFieldSet(
        new String[]{"test", ""},
        new String[]{"name", "value"}
    );

    // This succeeds in Spring Batch 5, fails in Spring Batch 6
    MyRecord result = mapper.mapFieldSet(fieldSet);

    assertNotNull(result);
    assertEquals("test", result.name());
    assertNull(result.value()); // null is a valid value for Long
}

Expected: MyRecord[name=test, value=null]

Actual: IllegalArgumentException: Cannot convert field 'value' to required type 'java.lang.Long'

Root cause

In RecordFieldSetMapper.mapFieldSet(), the following assertion was added:

Object converted = this.typeConverter.convertIfNecessary(fieldSet.readRawString(name), type);
Assert.notNull(converted, // ← rejects valid null for nullable types
      () -> String.format("Cannot convert field '%s' to required type '%s'", name, type.getName()));

The Assert.notNull check rejects all null values after conversion. This is too strict for reference types such as Long, Integer, BigDecimal, or LocalDate, where null may be valid. In Spring Batch 5, this assertion was not present.

// v5
args[i] = this.typeConverter.convertIfNecessary(fieldSet.readRawString(name), type);

Suggested fix

Remove the Assert.notNull check to restore the v5 behavior:

// Same as v5
args[i] = this.typeConverter.convertIfNecessary(fieldSet.readRawString(name), type);

Attached projects

The following zip files contain runnable MCVE projects. Run with mvn test.

spring-batch-mcve-v6.zip: Test fails with IllegalArgumentException

spring-batch-mcve-v5.zip: Test passes, value = null

To reproduce, run:

$ unzip spring-batch-mcve-v6.zip && cd spring-batch-mcve-v6
$ mvn test

Related

Commit: 62c4cbf ("Refine nullability checks with jSpecify")
Discussion

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions