clockworklabs/SpacetimeDB · error · RangeError

Timestamp is outside of the representable range of JS's Date

Error message

Timestamp is outside of the representable range of JS's Date

What it means

Timestamp.toDate() converts microseconds-since-epoch to JS Date milliseconds. The millis value must fit within +/-Number.MAX_SAFE_INTEGER (roughly +/-285,000 years); outside that, Number(millis) would silently lose precision, so the method throws RangeError instead.

Source

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

    const millis = date.getTime();
    const micros = BigInt(millis) * Timestamp.MICROS_PER_MILLIS;
    return new Timestamp(micros);
  }

  /**
   * Get a `Date` representing approximately the same point in time as `this`.
   *
   * This method truncates to millisecond precision,
   * and throws `RangeError` if the `Timestamp` is outside the range representable as a `Date`.
   */
  toDate(): Date {
    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 of JS's Date"
      );
    }
    return new Date(Number(millis));
  }

  /**
   * 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;

View on GitHub (pinned to 524b4487d9)

Solutions

  1. Fix the precision at the source: construct Timestamps from microseconds since the Unix epoch
  2. Range-check before converting and keep extreme values as Timestamp for comparisons instead of Date
  3. Prefer the microsecond accessors and toISOString-style formatting over Date when values may be extreme

Example fix

// before
const d = ts.toDate(); // extreme micros -> RangeError

// after
const MAX_MS = BigInt(Number.MAX_SAFE_INTEGER);
const ms = ts.__timestamp_micros_since_unix_epoch__ / 1000n;
const d = ms >= -MAX_MS && ms <= MAX_MS ? ts.toDate() : null; // null => handle out-of-band
Defensive patterns

Strategy: validation

Validate before calling

const MAX_SAFE_MILLIS = BigInt(Number.MAX_SAFE_INTEGER);
function timestampToDateSafe(ts: Timestamp): Date | null {
  const millis = ts.__timestamp_micros_since_unix_epoch__ / 1000n;
  if (millis > MAX_SAFE_MILLIS || millis < -MAX_SAFE_MILLIS) return null;
  return ts.toDate();
}

Prevention

When it happens

Trigger: Calling .toDate() on a Timestamp built from unit-confused or corrupted micros - e.g. nanoseconds passed as micros, or a value multiplied by 1000 twice - so that |micros / 1000n| exceeds 2^53-1.

Common situations: Hand-constructing Timestamps from server data in the wrong precision; BigInt arithmetic bugs (shift/multiply errors); test fixtures using sentinel values like Number.MAX_VALUE.

Related errors


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