apache/beam · error · TypeError

Cannot interpret as Duration.

Error message

Cannot interpret %s as Duration.

What it means

Duration.of() converts seconds/Timestamp-like inputs into a Duration, but a Timestamp represents an instant, not a span of time, so it is rejected with a TypeError rather than being silently misinterpreted as seconds.

Solutions

  1. Subtract two Timestamps to get a Duration: Duration.of(ts_end - ts_start).
  2. Pass a numeric seconds value or a Duration instance instead of a Timestamp.
  3. Check variable types before calling; a type guard like isinstance(x, Timestamp) can route to the right conversion.
  4. If you truly want the epoch-relative span, extract ts.micros or ts.seconds and construct Duration(micros=...) explicitly.

Example fix

// before
Duration.of(ts)  # TypeError
// after
Duration(micros=ts.micros)  # explicit epoch-relative duration
# or, for a span:
Duration.of(ts_end - ts_start)
Defensive patterns

Strategy: type-guard

Validate before calling

if isinstance(value, Timestamp):
    raise TypeError('got Timestamp where Duration expected')
duration = Duration.of(value)

Type guard

def is_duration_like(x):
    return isinstance(x, (Duration, int, float)) and not isinstance(x, Timestamp)

Try / catch

try:
    duration = Duration.of(value)
except TypeError:
    duration = Duration(micros=value.micros)  # or fix the caller

Prevention

When it happens

Trigger: Calling Duration.of(ts) or passing a Timestamp where a duration-like value is expected, e.g. Duration.of(seconds=some_timestamp), or __sub__/__mod__ paths that call Duration.of on a Timestamp operand.

Common situations: Confusing an event timestamp with a duration in pipeline code; passing the wrong variable (a Timestamp where a Duration or numeric seconds was intended); porting code between Timestamp and Duration APIs.

Understand the failure class

Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.

Related errors


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

Appendix: source

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

      seconds: Union[int, float] = 0,
      micros: Union[int, float] = 0) -> None:
    self.micros = int(seconds * 1000000) + int(micros)

  @staticmethod
  def of(seconds: DurationTypes) -> 'Duration':
    """Return the Duration for the given number of seconds since Unix epoch.

    If the input is already a Duration, the input itself will be returned.

    Args:
      seconds: Number of seconds as int, float or Duration.

    Returns:
      Corresponding Duration object.
    """

    if isinstance(seconds, Timestamp):
      raise TypeError('Cannot interpret %s as Duration.' % seconds)
    if isinstance(seconds, Duration):
      return seconds
    return Duration(seconds)

  def to_proto(self) -> duration_pb2.Duration:
    """Returns the `google.protobuf.duration_pb2` representation."""
    secs = self.micros // 1000000
    nanos = (self.micros % 1000000) * 1000
    return duration_pb2.Duration(seconds=secs, nanos=nanos)

  @staticmethod
  def from_proto(duration_proto: duration_pb2.Duration) -> 'Duration':
    """Creates a Duration from a `google.protobuf.duration_pb2`.

    Note that the google has a sub-second resolution of nanoseconds whereas this
    class has a resolution of microsends. This class will truncate the
    nanosecond resolution down to the microsecond.
    """

View on GitHub (pinned to 12126d8942)