apache/beam · error · ValueError

Could not parse RFC 3339 string

Error message

Could not parse RFC 3339 string '%s': fractional seconds beyond nanosecond precision are not supported.

What it means

After isoparse succeeds, from_rfc3339() scans the raw string for fractional-second digits; if more than NANOS_PRECISION (9) digits are present (sub-nanosecond), the value cannot be represented and a ValueError is raised rather than silently losing precision.

Solutions

  1. Truncate the fractional part to at most 9 digits before parsing: s[:idx+10]
  2. Round the value to nanoseconds and reconstruct the string
  3. If full fidelity is needed, keep the extra digits in a separate field and parse only the first 9 with from_rfc3339

Example fix

// before
ts = Timestamp.from_rfc3339('2024-01-01T00:00:00.123456789012Z')
// after
m = re.match(r'(.*\.\d{9})\d+', s)
ts = Timestamp.from_rfc3339(m.group(1) + 'Z') if m else Timestamp.from_rfc3339(s)
Defensive patterns

Strategy: validation

Validate before calling

m = re.search(r'[.,](\d+)', s)
if m and len(m.group(1)) > 9:
    s = re.sub(r'([.,]\d{9})\d+', r'\1', s)

Try / catch

try:
    ts = Timestamp.from_rfc3339(s)
except ValueError as e:
    if 'beyond nanosecond' in str(e):
        ts = Timestamp.from_rfc3339(re.sub(r'([.,]\d{9})\d+', r'\1', s))
    else:
        raise

Prevention

When it happens

Trigger: Timestamp.from_rfc3339('2024-01-01T00:00:00.123456789012Z') — 12 fractional digits exceed nanosecond precision.

Common situations: Ingesting scientific/instrument timestamps with picosecond or femtosecond fractions; concatenating a performance-counter suffix onto an RFC 3339 string; Java Instant or chrono APIs emitting >9 digits.

Understand the failure class

Background: "value must be between 0 and 1" / "out of range" / "must not be negative" errors: fixing range-validation failures across open-source libraries — this error's family across 42 libraries.

Related errors


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

Appendix: source

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

      All timezones are implicitly converted to UTC.

    Args:
      rfc3339: String in RFC 3339 form.
    """
    try:
      dt = dateutil.parser.isoparse(rfc3339).astimezone(pytz.UTC)
    except ValueError as e:
      raise ValueError(
          "Could not parse RFC 3339 string '{}' due to error: '{}'.".format(
              rfc3339, e))
    timestamp = cls.from_utc_datetime(dt)
    # dateutil silently truncates fractional seconds to microseconds; parse
    # any sub-microsecond digits ourselves to avoid losing precision.
    fraction = re.search(r'[0-9]{2}[.,]([0-9]{7,})', rfc3339)
    if fraction:
      digits = fraction.group(1)
      if len(digits) > cls.NANOS_PRECISION:
        raise ValueError(
            "Could not parse RFC 3339 string '%s': fractional seconds "
            'beyond nanosecond precision are not supported.' % rfc3339)
      precision = len(digits)
      sub_micro = int(digits[cls.MICROS_PRECISION:])
      return Timestamp(
          timestamp.seconds(),
          timestamp.subseconds() * _POW_10[precision - cls.MICROS_PRECISION] +
          sub_micro,
          precision)
    return timestamp

  def seconds(self) -> int:
    """Returns the timestamp in seconds."""
    return self._seconds

  def subseconds(self) -> int:
    """Returns the fraction of a second, in units of 10**-precision seconds.

View on GitHub (pinned to 12126d8942)