apache/beam · error · IllegalArgumentException

value is out of range for Decimal(, )

Error message

value  is out of range for Decimal(, )

What it means

Thrown by ClickHouseWriter.truncateAndCheckDecimal when a BigDecimal, after truncation to the column scale, still exceeds the maximum magnitude of a ClickHouse Decimal of the given precision (unscaled abs value >= 10^precision). The writer rejects values that would not fit rather than silently corrupting data.

Solutions

  1. Pre-truncate or rescale the value before writing so it fits the declared precision/scale.
  2. Alter the ClickHouse column to a wider type (Decimal64/Decimal128) if larger values are legitimate.
  3. Validate upstream data ranges against the target precision before the write.
  4. Align the Beam schema's Decimal precision/scale with the ClickHouse table definition.

Example fix

// before
writer.decimalValue(new BigDecimal("100000")); // Decimal(5,0) max is 99999
// after
BigDecimal v = new BigDecimal("100000").setScale(0, RoundingMode.DOWN);
if (v.abs().compareTo(new BigDecimal("99999")) > 0) { throw new ValidationException("too large"); }
writer.decimalValue(v);
Defensive patterns

Strategy: validation

Validate before calling

import java.math.*;
boolean fitsClickHouseDecimal(BigDecimal value, int precision, int scale) {
  BigDecimal truncated = value.setScale(scale, RoundingMode.DOWN);
  BigDecimal max = BigDecimal.TEN.pow(precision).subtract(BigDecimal.ONE);
  return truncated.abs().compareTo(max) <= 0;
}

Type guard

boolean isBigDecimal(Object v) { return v instanceof java.math.BigDecimal; }

Try / catch

try {
  writer.decimalValue(bigDecimalValue);
} catch (IllegalArgumentException e) {
  if (e.getMessage() != null && e.getMessage().contains("out of range for Decimal")) {
    throw new DataException("Value exceeds ClickHouse Decimal column capacity: " + e.getMessage());
  }
  throw e;
}

Prevention

When it happens

Trigger: Calling writeValue/decimalValue with a BigDecimal whose integer part has more digits than the target Decimal precision allows — e.g. 100000 into Decimal(5,0) (max 99999), or 123.456 into Decimal(4,2).

Common situations: Aggregations (SUM, multiplication) producing values larger than the declared column precision; mismatch between the Beam Row schema and the actual ClickHouse table; ingesting source data whose scale/precision assumptions changed.

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 apache/beam@12126d8942 (2026-09-13). Data as JSON: /api/errors/5a8e2b275f8647b1. Report an issue: GitHub.

Appendix: source

Thrown at sdks/java/io/clickhouse/src/main/java/org/apache/beam/sdk/io/clickhouse/ClickHouseWriter.java:75

    }
  }

  /**
   * Truncates a value to a {@code Decimal(precision, scale)} column's scale and checks it against
   * the column's declared range.
   *
   * <p>Excess fractional digits are discarded toward zero, matching ClickHouse's own behavior and
   * the truncation {@link BinaryStreamUtils#writeDecimal} would otherwise apply. The result is then
   * bounded by the declared precision: {@code writeDecimal} only range-checks against the backing
   * storage width (32/64/128/256 bits, selected from the precision), which is wider than the
   * declared type, and ClickHouse's RowBinary reader does not re-check. Without this, a value such
   * as {@code 100000} would be stored in a {@code Decimal(5, 0)} column whose declared maximum is
   * {@code 99999}.
   */
  static BigDecimal truncateAndCheckDecimal(BigDecimal value, int precision, int scale) {
    BigDecimal truncated = value.setScale(scale, RoundingMode.DOWN);
    if (truncated.unscaledValue().abs().compareTo(DECIMAL_BOUNDS[precision]) >= 0) {
      throw new IllegalArgumentException(
          "value " + value + " is out of range for Decimal(" + precision + ", " + scale + ")");
    }
    return truncated;
  }

  /**
   * Encodes a timestamp into ClickHouse's {@code DateTime64(precision)} representation: a signed
   * 64-bit integer counting ticks of size 10<sup>-precision</sup> seconds since the Unix epoch.
   *
   * <p>Accepts either a Joda {@link ReadableInstant} (millisecond precision) or a {@link
   * java.time.Instant} (nanosecond precision). Sub-tick fractions are truncated toward negative
   * infinity, matching ClickHouse's own encoding for negative timestamps.
   */
  static long encodeDateTime64(Object value, int precision) {
    long epochSecond;
    int nanoOfSecond;
    if (value instanceof java.time.Instant) {
      java.time.Instant inst = (java.time.Instant) value;

View on GitHub (pinned to 12126d8942)