apache/beam · error · ValueError

The remainder of %r modulo %r has sub-microsecond…

Error message

The remainder of %r modulo %r has sub-microsecond precision, which Duration cannot represent. Truncate this timestamp with to_precision(6, allow_lossy_conversion=True) first.

What it means

Timestamp.__mod__ computes the timestamp modulo a Duration at nanosecond resolution and returns a Duration in microseconds. If the remainder has sub-microsecond digits it cannot be represented as a Duration, so a ValueError is raised telling you to truncate the timestamp first.

Solutions

  1. Truncate the timestamp before the modulo: ts.to_precision(6, allow_lossy_conversion=True) % duration.
  2. Compute the remainder with `.nanos` manually and decide how to handle sub-microsecond digits.
  3. Ensure input timestamps are constructed/normalized to microsecond precision at ingest.
  4. Catch ValueError and re-raise with context about the specific timestamp/duration pair.

Example fix

// before
offset = ts % Duration(seconds=30)
// after
offset = ts.to_precision(6, allow_lossy_conversion=True) % Duration(seconds=30)
Defensive patterns

Strategy: try-catch

Validate before calling

def safe_mod(ts, dur):
    r = ts.nanos % (dur.micros * 1000)
    if r % 1000:
        raise ValueError('sub-microsecond remainder')
    return Duration(micros=r // 1000)

Try / catch

try:
    offset = ts % window
except ValueError:
    offset = ts.to_precision(6, allow_lossy_conversion=True) % window

Prevention

When it happens

Trigger: Evaluating ts % duration (or ts % Duration.of(...)) where ts has nanosecond precision and the nanosecond remainder mod (duration.micros*1000) is not a multiple of 1000.

Common situations: Windowing/field-masking logic like ts % window_duration on high-precision timestamps; rate limiting or bucketing arithmetic ported from microsecond-precision data to nanosecond-precision data.

Related errors


AI-assisted analysis of apache/beam@12126d8942 (2026-09-13). Data as JSON: /api/errors/6fa1304953e2b4c2. Report an issue: GitHub.

Appendix: source

Thrown at sdks/python/apache_beam/utils/timestamp.py:470

        raise ValueError(
            'The difference of %r and %r has sub-microsecond precision, '
            'which Duration cannot represent. Truncate the operands with '
            'to_precision(6, allow_lossy_conversion=True) first.' %
            (self, other))
      return Duration(micros=diff_nanos // 1000)
    other = Duration.of(other)
    precision = max(self._precision, Timestamp.MICROS_PRECISION)
    return Timestamp(
        subseconds=self._total(precision) -
        other.micros * _POW_10[precision - Timestamp.MICROS_PRECISION],
        precision=precision)

  def __mod__(self, other: DurationTypes) -> 'Duration':
    other = Duration.of(other)
    remainder_nanos = self._total(Timestamp.NANOS_PRECISION) % (
        other.micros * 1000)
    if remainder_nanos % 1000 != 0:
      raise ValueError(
          'The remainder of %r modulo %r has sub-microsecond precision, '
          'which Duration cannot represent. Truncate this timestamp with '
          'to_precision(6, allow_lossy_conversion=True) first.' % (self, other))
    return Duration(micros=remainder_nanos // 1000)


MIN_TIMESTAMP = Timestamp(
    micros=int(common_urns.constants.MIN_TIMESTAMP_MILLIS.constant) * 1000)
MAX_TIMESTAMP = Timestamp(
    micros=int(common_urns.constants.MAX_TIMESTAMP_MILLIS.constant) * 1000)


class Duration(object):
  """Represents a second duration with microsecond granularity.

  Can be treated in common arithmetic operations as a numeric type.

  Internally stores a time interval as an int of microseconds. This strategy

View on GitHub (pinned to 12126d8942)