Hmbown/CodeWhale · error · Error

Invalid : expected a finite, safely representable number.

Error message

Invalid ${field}: expected a finite, safely representable number.

What it means

The `number` helper rejects time values that are not finite, safely representable numbers: strings, NaN, Infinity, or integers beyond Number.MAX_SAFE_INTEGER (pet/src/core/ingest.ts:68). Times must be numeric milliseconds.

Solutions

  1. Coerce time fields to finite epoch-millisecond numbers before ingest
  2. Convert nanosecond timestamps with Number(BigInt(ns)/1_000_000n) or use the ns() decimal-string path
  3. Validate with typeof v==='number' && Number.isFinite(v) upstream

Example fix

// before
startTime: '1700000000000'
// after
startTime: Number('1700000000000')
Defensive patterns

Strategy: type-guard

Validate before calling

const validTime=(v:unknown)=>typeof v==='number'&&Number.isFinite(v)&&Math.abs(v)<=Number.MAX_SAFE_INTEGER;

Type guard

const isEpochMs=(v:unknown): v is number => typeof v==='number'&&Number.isFinite(v)&&Math.abs(v)<=Number.MAX_SAFE_INTEGER;

Try / catch

try { ingest(records); } catch (e) { if (/Invalid .* expected a finite/.test(e.message)) { /* coerce string/NaN times to numbers or drop record */ } else throw e; }

Prevention

When it happens

Trigger: Passing a startTime/endTime as a string ('1700000000000'), NaN, Infinity, or a float/integer exceeding Number.MAX_SAFE_INTEGER to the ingest normalizer.

Common situations: JSON transports that serialized times as strings; using Date ISO strings instead of epoch millis; microsecond/nanosecond epoch values overflowing the safe-integer range.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@433685b202 (2026-09-15). Data as JSON: /api/errors/0296923ee564b359. Report an issue: GitHub.

Appendix: source

Thrown at pet/src/core/ingest.ts:68

  return hook ? hook(out, path) : out;
}
export function privacyEvent(e: WhaleEvent, mode: PrivacyMode): WhaleEvent {
  if (mode === 'metadata') {
    const { payload: _payload, raw: _raw, ...rest } = e;
    return { ...rest, links: e.links?.map(link=>({traceId:link.traceId,spanId:link.spanId})), attributes: Object.fromEntries(Object.entries(e.attributes).filter(([k, v]) =>
      SAFE_META.test(k) && (typeof v !== 'object' || v === null)
      || ['whalesong.container', 'codewhale.container', 'whalesong.waiting'].includes(k) && typeof v === 'boolean'
      // Relative timestamps can be negative before the importer rebases them.
      || k === 'whalesong.error_onset_ms' && typeof v === 'number' && Number.isFinite(v))) };
  }
  return e;
}
function number(v: unknown, field: string, optional = true): number | undefined {
  if (v === undefined || v === null) {
    if (optional) return undefined;
    throw new Error(`Missing ${field}. Times must be numeric milliseconds.`);
  }
  if (typeof v !== 'number' || !Number.isFinite(v) || Math.abs(v) > Number.MAX_SAFE_INTEGER) throw new Error(`Invalid ${field}: expected a finite, safely representable number.`);
  return v;
}
function nonnegative(v: unknown, field: string): number | undefined {
  const n = number(v, field); if (n !== undefined && n < 0) throw new Error(`${field} must be nonnegative.`); return n;
}
function numericAttr(v: unknown): number | undefined {
  if (v === undefined || v === null || v === '') return undefined;
  const n = Number(v); return Number.isFinite(n) && n >= 0 ? n : undefined;
}
export function categoryFor(name: string, a: Obj): Category {
  const explicit = a['whalesong.category'] ?? a.category;
  if (CATEGORIES.includes(explicit)) return explicit;
  const n = name.toLowerCase(), op = String(a['gen_ai.operation.name'] ?? '').toLowerCase();
  if (/exception|^error\b/.test(n)) return 'error';
  if (/spawn|fork|subagent/.test(n) || op === 'invoke_agent') return 'agent';
  if (/message\.send|handoff|agent\.message/.test(n)) return 'communication';
  if (/retrieve|retrieval|context|embedding|vector|memory|rag/.test(n)) return 'memory';
  if (/browser|navigate|screenshot|click|playwright/.test(n)) return 'browser';

View on GitHub (pinned to 433685b202)