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
- Truncate the timestamp before the modulo: ts.to_precision(6, allow_lossy_conversion=True) % duration.
- Compute the remainder with `.nanos` manually and decide how to handle sub-microsecond digits.
- Ensure input timestamps are constructed/normalized to microsecond precision at ingest.
- 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
- Normalize timestamps to microsecond precision before modulo/window math.
- Use `.nanos` arithmetic if sub-microsecond remainders matter to your logic.
- Document the truncation policy for windowing code.
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
- The difference of %r and %r has sub-microsecond precision…
- Cannot convert from nanoseconds to microseconds because…
- Converting %r to datetime truncates it to microseconds. Set…
- %r cannot be represented exactly at precision
- %r has greater than microsecond precision, converting it to…
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 strategyView on GitHub (pinned to 12126d8942)