Hmbown/CodeWhale · error · Error
Invalid pet duration.
Error message
Invalid pet duration.
What it means
Guard at the top of compilePetTelemetry: fires when the durationMs argument is not a finite number or is negative (NaN, Infinity, or < 0). The caller passed an invalid trace duration; only a finite non-negative duration can be divided into pet buckets.
Solutions
- Clamp the duration: Math.max(0, endMs - startMs) and verify Number.isFinite before calling
- Default missing end timestamps to start (zero duration) or to now
- Validate timestamps at the span-producing layer so bad spans never reach compile
- Skip spans with non-finite timestamps and log them
Example fix
// before compilePetTelemetry(events, endMs - startMs); // NaN if startMs undefined // after const duration = Number.isFinite(endMs - startMs) ? Math.max(0, endMs - startMs) : 0; compilePetTelemetry(events, duration);
Defensive patterns
Strategy: validation
Validate before calling
const duration = endMs != null && Number.isFinite(endMs - startMs) ? Math.max(0, endMs - startMs) : 0;
if (!Number.isFinite(duration) || duration < 0) throw new Error('bad duration before compile'); Type guard
function isValidDuration(ms: unknown): ms is number {
return typeof ms === 'number' && Number.isFinite(ms) && ms >= 0;
} Try / catch
try {
compilePetTelemetry(events, duration);
} catch (e) {
if (e.message === 'Invalid pet duration.') {
compilePetTelemetry(events, 0); // treat as zero-duration window
} else throw e;
} Prevention
- Default missing span ends to the start time or to now
- Guard all timestamp subtraction with Number.isFinite
- Clamp with Math.max(0, ...) before passing durations
When it happens
Trigger: Passing a durationMs that is NaN (e.g. from a failed Date arithmetic or undefined subtraction), negative (end before start), or Infinity (missing end timestamp).
Common situations: Computing duration as Date.now() - undefinedStart; a span whose end timestamp was absent so subtraction yielded NaN; timezone/clock adjustments producing negative windows; JSON round-trips turning a number into null then NaN via coercion.
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
- Invalid pet duration.
- Invalid Engine pet clock.
- Invalid Engine pet metadata.
- Invalid Engine pet metadata.
- Invalid Engine pet metadata fields.
AI-assisted analysis of Hmbown/CodeWhale@433685b202 (2026-09-15).
Data as JSON: /api/errors/c7f8a63e7dcff004.
Report an issue: GitHub.
Appendix: source
Thrown at pet/src/core/pet-telemetry.ts:72
try { packet = JSON.parse(line); validatePetBucket(packet); }
catch (error) { this.reset(); throw error; }
const previous = this.sequence; this.sequence = packet.sequence;
if (previous === undefined || packet.sequence <= previous) return;
return packet;
}
}
const order = (a: string, b: string) => a < b ? -1 : a > b ? 1 : 0;
const keyOf = (e: WhaleEvent) => JSON.stringify([e.traceId, e.id]);
const isContainer = (e: WhaleEvent) => e.attributes['whalesong.container'] === true
|| e.attributes['codewhale.container'] === true;
/** Compile a single trace. Unknown-duration spans provide onsets, not occupancy.
* Updates of the same trace/id replace earlier snapshots rather than double count.
* An endpoint onset gets its own bucket; intervals use [start, end). */
export function compilePetTelemetry(input: readonly WhaleEvent[], durationMs = 0, firstSequence = 0, originMs = 0): PetBucket[] {
if (input.length > 250_000) throw new Error('Pet input exceeds 250000 events.');
if (!Number.isFinite(durationMs) || durationMs < 0) throw new Error('Invalid pet duration.');
if (!Number.isSafeInteger(firstSequence) || firstSequence < 0) throw new Error('Invalid first pet bucket.');
if (!Number.isFinite(originMs)) throw new Error('Invalid pet clock origin.');
durationMs = Math.max(0, durationMs - originMs);
const unique = new Map<string, WhaleEvent>();
const traces = new Set<string>();
for (const e of input) {
if (e.schemaVersion !== 1 || !e.id || !e.traceId || !CATEGORIES.includes(e.category)
|| !Number.isFinite(e.startTime) || !Number.isFinite(e.endTime)
|| e.startTime < 0 || e.endTime < e.startTime || !e.attributes)
throw new Error('Invalid event-v1 pet input. Import through importTrace first.');
traces.add(e.traceId); unique.set(keyOf(e), e);
}
if (traces.size > 1) throw new Error('Select one trace for the pet.');
const parents = new Set([...unique.values()].filter(e => e.parentId).map(e => e.parentId!));
const events = [...unique.values()].filter(e => !isContainer(e)
&& !(e.category === 'orchestration' && parents.has(e.id)))
.map(e => ({ ...e, startTime: e.startTime - originMs, endTime: (e.openEnded ? e.startTime : e.endTime) - originMs,
attributes: e.attributes['whalesong.error_onset_ms'] === undefined ? e.attributesView on GitHub (pinned to 433685b202)