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

  1. Truncate both operands first: ts1.to_precision(6, allow_lossy_conversion=True) - ts2.to_precision(6, allow_lossy_conversion=True).
  2. Use `.nanos` on both timestamps and compute the difference as an integer nanosecond value yourself.
  3. Construct the source timestamps at microsecond precision so differences are always representable.
  4. 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

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


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)