pentaho/pentaho-kettle · error · IllegalArgumentException

The date has too much day from epoch day!

Error message

The date has too much day from epoch day!

What it means

During Avro record creation, converting a Kettle date/timestamp field to an Avro epoch-day value triggers an ArithmeticException (integer overflow in day computation), which createAvroRecord wraps as IllegalArgumentException('The date has too much day from epoch day!'). It signals a date value too extreme to represent as an Avro date.

Solutions

  1. Inspect the offending row's date value and fix it upstream (correct year/epoch unit)
  2. Verify the field's Avro logical type mapping (date vs timestamp-millis) matches the Kettle value type
  3. Add a filter/invalid-rows handling step before the Avro output to divert rows with out-of-range dates
  4. Catch IllegalArgumentException from write() and log the row for correction

Example fix

// before
row.getDate( "event_date" ) // contains year 999999999 -> ArithmeticException on toEpochDay()
// after
// sanitize/validate before writing
LocalDate d = row.getDate( "event_date" ).toInstant().atZone( ZoneOffset.UTC ).toLocalDate();
if ( d.getYear() < 1 || d.getYear() > 9999 ) { d = LocalDate.of( 9999, 12, 31 ); }
Defensive patterns

Strategy: validation

Validate before calling

Date d = row.getDate( fieldName );
long year = d == null ? 0 : d.toInstant().atZone( ZoneOffset.UTC ).getYear();
if ( year < 1 || year > 9999 ) {
  throw new ValidationException( fieldName + " year out of representable range: " + year );
}

Try / catch

try {
  writer.write( row );
} catch ( IllegalArgumentException e ) {
  logError( "Unwritable date row: " + e.getMessage(), e );
  // route row to error stream
}

Prevention

When it happens

Trigger: Writing a row whose date/timestamp field, when converted to epoch days (long/days conversion in the field-mapping switch), overflows — e.g. a year like 999999999 or a null-parsed garbage value producing a huge millisecond count.

Common situations: Bad input data in a date column (typo'd year, sentinel dates like 0001-01-01 mishandled, epoch in wrong unit — seconds passed as milliseconds); inconsistent source-type mapping between timestamp and date fields.

Understand the failure class

Background: "value must be between 0 and 1" / "out of range" / "must not be negative" errors: fixing range-validation failures across open-source libraries — this error's family across 42 libraries.

Related errors


AI-assisted analysis of pentaho/pentaho-kettle@f3058517a1 (2026-09-13). Data as JSON: /api/errors/768f0f732f0c44f6. Report an issue: GitHub.

Appendix: source

Thrown at plugins/avro-format/core/src/main/java/org/pentaho/di/trans/steps/avro/output/PentahoAvroRecordWriter.java:225

            case TIMESTAMP_MILLIS, TIMESTAMP_MICROS, TIMESTAMP_NANOS:
              Timestamp defaultTimestamp = null;
              if ( defaultValue != null && !defaultValue.isEmpty() ) {
                String conversionMask =
                  ( vmi.getConversionMask() == null ) ? ValueMetaBase.DEFAULT_TIMESTAMP_PARSE_MASK
                    : vmi.getConversionMask();
                DateTimeFormatter fmt = DateTimeFormatter.ofPattern( conversionMask );
                LocalDateTime ldt = LocalDateTime.parse( defaultValue, fmt );
                defaultTimestamp = Timestamp.valueOf( ldt );
              }
              Timestamp timeStamp = (Timestamp) row.getDate( fieldMetaIndex, defaultTimestamp );
              outputRecord.put( avroFieldName, AvroTimestampHandler.fromTimestamp( timeStamp, avroType ) );
              break;
          }

        }
      }
    } catch ( ArithmeticException e ) {
      throw new IllegalArgumentException( "The date has too much day from epoch day!", e );
    } catch ( KettleValueException e ) {
      throw new IllegalArgumentException( "some exception while writing avro", e );
    }
    return outputRecord;
  }

  private double applyScale( double number, IAvroOutputField outputField ) {
    if ( outputField.getScale() > 0 ) {
      BigDecimal bd = new BigDecimal( number );
      bd = bd.setScale( outputField.getScale(), BigDecimal.ROUND_HALF_UP );
      number = bd.doubleValue();
    }
    return number;
  }

  private float applyScale( float number, IAvroOutputField outputField ) {
    if ( outputField.getScale() > 0 ) {
      BigDecimal bd = new BigDecimal( number );

View on GitHub (pinned to f3058517a1)