apache/beam · error · ValueError
The difference of %r and %r has sub-microsecond precision…
Error message
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.
What it means
Subtracting one Timestamp from another computes the difference at nanosecond resolution, but Duration only stores microseconds. If the difference has sub-microsecond digits, Timestamp.__sub__ raises ValueError because a Duration cannot represent it exactly.
Solutions
- Truncate both operands first: ts1.to_precision(6, allow_lossy_conversion=True) - ts2.to_precision(6, allow_lossy_conversion=True).
- Use `.nanos` on both timestamps and compute the difference as an integer nanosecond value yourself.
- Construct the source timestamps at microsecond precision so differences are always representable.
- Wrap the subtraction in try/except ValueError and fall back to a truncated-difference computation.
Example fix
// before
delta = ts_end - ts_start # ValueError for sub-microsecond diff
// after
delta = (ts_end.to_precision(6, allow_lossy_conversion=True) -
ts_start.to_precision(6, allow_lossy_conversion=True)) Defensive patterns
Strategy: try-catch
Validate before calling
def safe_sub(a, b):
diff = a.nanos - b.nanos
if diff % 1000:
raise ValueError(f'{diff} ns not representable as Duration micros')
return Duration(micros=diff // 1000) Try / catch
try:
delta = ts_end - ts_start
except ValueError:
delta = (ts_end.to_precision(6, allow_lossy_conversion=True) -
ts_start.to_precision(6, allow_lossy_conversion=True)) Prevention
- Compute with `.nanos` when operands may carry nanosecond precision.
- Truncate to micros at ingest so timestamp arithmetic is always lossless.
- Never assume Timestamp differences are microsecond-aligned.
When it happens
Trigger: Evaluating ts1 - ts2 where the two timestamps differ by a non-multiple of 1000 nanoseconds, e.g. subtracting two nanosecond-precision timestamps such as Timestamp(0, 1_000_000_001, 9) - Timestamp(0, 0, 9).
Common situations: Measuring event-to-event latencies from nanosecond-resolution sources; test code computing expected durations from high-precision timestamps; porting code that previously only used microsecond timestamps.
Related errors
- The remainder of %r modulo %r has sub-microsecond…
- 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/b053b5e16cc4bb22.
Report an issue: GitHub.
Appendix: source
Thrown at sdks/python/apache_beam/utils/timestamp.py:452
return self + other
@overload
def __sub__(self, other: DurationTypes) -> 'Timestamp':
pass
@overload
def __sub__(self, other: 'Timestamp') -> 'Duration':
pass
def __sub__(
self, other: Union[DurationTypes,
'Timestamp']) -> Union['Timestamp', 'Duration']:
if isinstance(other, Timestamp):
diff_nanos = (
self._total(Timestamp.NANOS_PRECISION) -
other._total(Timestamp.NANOS_PRECISION))
if diff_nanos % 1000 != 0:
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(View on GitHub (pinned to 12126d8942)