clockworklabs/SpacetimeDB · error · RangeError

Timestamp is outside of the representable range for ISO stri

Error message

Timestamp is outside of the representable range for ISO string formatting

What it means

Timestamp.toISOString() formats with full microsecond precision by reusing new Date(Number(millis)).toISOString() and replacing the millisecond digits with the microsecond remainder. The same safe-integer guard as toDate applies: millis beyond +/-Number.MAX_SAFE_INTEGER cannot round-trip through a Date without losing precision, so it throws RangeError.

Source

Thrown at crates/bindings-typescript/src/lib/timestamp.ts:133

  }

  /**
   * Get an ISO 8601 / RFC 3339 formatted string representation of this timestamp with microsecond precision.
   *
   * This method preserves the full microsecond precision of the timestamp,
   * and throws `RangeError` if the `Timestamp` is outside the range representable in ISO format.
   *
   * @returns ISO 8601 formatted string with microsecond precision (e.g., '2025-02-17T10:30:45.123456Z')
   */
  toISOString(): string {
    const micros = this.__timestamp_micros_since_unix_epoch__;
    const millis = micros / Timestamp.MICROS_PER_MILLIS;

    if (
      millis > BigInt(Number.MAX_SAFE_INTEGER) ||
      millis < BigInt(Number.MIN_SAFE_INTEGER)
    ) {
      throw new RangeError(
        'Timestamp is outside of the representable range for ISO string formatting'
      );
    }

    const date = new Date(Number(millis));
    const isoBase = date.toISOString(); // Format: '2025-02-17T10:30:45.123Z'

    // Extract the full 6 decimal places of microseconds
    const microsRemainder = Math.abs(Number(micros % 1000000n));
    const fractionalPart = String(microsRemainder).padStart(6, '0');

    // Replace the 3-digit millisecond part with the full 6-digit microsecond part
    return isoBase.replace(/\.\d{3}Z$/, `.${fractionalPart}Z`);
  }

  since(other: Timestamp): TimeDuration {
    return new TimeDuration(
      this.__timestamp_micros_since_unix_epoch__ -

View on GitHub (pinned to 524b4487d9)

Solutions

  1. Fix the precision at the source: microseconds since the Unix epoch
  2. Range-check millis before formatting and fall back to raw micros in the display string
  3. Keep comparisons on Timestamp values instead of round-tripping through Date/ISO strings

Example fix

// before
const s = ts.toISOString(); // extreme micros -> RangeError

// after
const MAX_MS = BigInt(Number.MAX_SAFE_INTEGER);
const ms = ts.__timestamp_micros_since_unix_epoch__ / 1000n;
const s = ms >= -MAX_MS && ms <= MAX_MS
  ? ts.toISOString()
  : `${ts.__timestamp_micros_since_unix_epoch__}us`; // lossless fallback
Defensive patterns

Strategy: validation

Validate before calling

const MAX_SAFE_MILLIS = BigInt(Number.MAX_SAFE_INTEGER);
function timestampToIsoSafe(ts: Timestamp): string {
  const millis = ts.__timestamp_micros_since_unix_epoch__ / 1000n;
  if (millis > MAX_SAFE_MILLIS || millis < -MAX_SAFE_MILLIS) {
    return `${ts.__timestamp_micros_since_unix_epoch__}us`;
  }
  return ts.toISOString();
}

Prevention

When it happens

Trigger: Calling .toISOString() on a Timestamp whose micros, divided by 1000n, fall outside +/-2^53-1 - typically unit-confused input (millis multiplied by 1e6 twice) or corrupted values.

Common situations: Formatting timestamps received from feeds with different epochs or units; BigInt arithmetic bugs; log/audit code that assumes every timestamp is 21st-century.

Related errors


AI-assisted analysis of clockworklabs/SpacetimeDB@524b4487d9 (2026-08-16). Data as JSON: /api/errors/570e20009c6a361d. Report an issue: GitHub.