{"record":{"id":"8008a4355750d028","repo":"apache/beam","slug":"matchcontinuously-stop-timestamp-s-precedes-start-timestamp","errorCode":null,"errorMessage":"MatchContinuously stop_timestamp %s precedes start_timestamp %s","messagePattern":"MatchContinuously stop_timestamp (.+?) precedes start_timestamp (.+?)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"sdks/python/apache_beam/io/fileio.py","lineNumber":473,"sourceCode":"    # Apply windowing last because dedup relies on the global window.\n    if self.apply_windowing:\n      match_files = match_files | beam.WindowInto(FixedWindows(self.interval))\n\n    return match_files\n\n  def _match_deduplicated(self,\n                          pbegin) -> beam.PCollection[filesystem.FileMetadata]:\n    # Watch emits each file once per dedup key: the path, joined by the mtime\n    # when matching updated files. stop_timestamp bounds the polls to\n    # [start, stop).\n    clock = _PollClock()\n    if self.stop_ts == MAX_TIMESTAMP:\n      termination = never()\n    else:\n      start_ts = Timestamp.of(self.start_ts)\n      stop_ts = Timestamp.of(self.stop_ts)\n      if stop_ts < start_ts:\n        raise ValueError(\n            'MatchContinuously stop_timestamp %s precedes start_timestamp %s' %\n            (stop_ts, start_ts))\n      interval_micros = Duration.of(self.interval).micros\n      span_micros = (stop_ts - start_ts).micros\n      # Ceiling division reproduces PeriodicImpulse's tick count; the window\n      # upper bound is exclusive.\n      max_polls = -(-span_micros // interval_micros)\n      if max_polls == 0:\n        # An empty [start, stop) window never ticks; the impulse path keeps\n        # the output empty without Watch's unconditional first poll.\n        return self._match_all_each_poll(pbegin)\n      termination = _WatchWindowTermination(clock, start_ts.micros, max_polls)\n    poll_fn = _MatchContinuouslyPollFn(\n        self.empty_match_treatment,\n        self.start_ts,\n        clock,\n        mtime_timestamps=self.timestamp_cursor)\n    # The key coder is inferred from the key function's return annotation.","sourceCodeStart":455,"sourceCodeEnd":491,"githubUrl":"https://github.com/apache/beam/blob/12126d8942aaf848030c478b4c6a28c6af861c66/sdks/python/apache_beam/io/fileio.py#L455-L491","documentation":"When MatchContinuously has a bounded stop_timestamp, _match_deduplicated computes the number of poll windows between start and stop. If stop_timestamp is earlier than start_timestamp the window span is negative and no valid tick plan exists, so Beam raises ValueError during pipeline expansion.","triggerScenarios":"Constructing MatchContinuously(..., start_timestamp=ts1, stop_timestamp=ts2) where ts2 < ts1, then running expand() (has_deduplication path).","commonSituations":"Computing start/stop times from relative offsets ('now - 1h' as stop vs 'now' as start), swapping the arguments, or timezone confusion causing the stop time to precede the start.","solutions":["Ensure stop_timestamp is later than start_timestamp; swap the values if they were passed in the wrong order.","If you want an unbounded match, omit stop_timestamp (defaults to MAX_TIMESTAMP / never terminates).","Add an assertion or min/max normalization on the two timestamps before constructing the transform."],"exampleFix":"# before\nMatchContinuously('/data/*', start_timestamp=now, stop_timestamp=now - timedelta(hours=1))\n# after\nstart, stop = now - timedelta(hours=1), now\nMatchContinuously('/data/*', start_timestamp=start, stop_timestamp=stop)","handlingStrategy":"validation","validationCode":"if stop_ts is not None and stop_ts < start_ts:\n    raise ValueError('stop_timestamp must be >= start_timestamp')","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Compute start/stop from a single 'now' reference to avoid ordering bugs.","Normalize with start, stop = min(a,b), max(a,b) when inputs may be unordered."],"tags":["python","apache-beam","io","timestamps"],"backgroundTag":"invalid-argument-value","analyzedSha":"12126d8942aaf848030c478b4c6a28c6af861c66","analyzedAt":"2026-09-13T01:50:10.254Z","contentChangedAt":"2026-09-13T01:50:10.254Z","schemaVersion":2},"datasetVersion":"2026-09-14T16:17:12.679Z"}