Hmbown/CodeWhale · error · Error

Select one trace for the pet.

Error message

Select one trace for the pet.

What it means

Generic guard error meaning the pet view was asked to render without a single trace being selected. The pet visualization requires exactly one trace as its subject; this fires when zero (or ambiguous multiple) traces are available/selected, telling the caller to narrow the selection first.

Solutions

  1. Filter input to a single traceId before calling compilePetTelemetry.
  2. If the input came from a UI export, select only one trace.
  3. Group events by traceId and compile each group separately.

Example fix

// before
compilePetTelemetry(allEvents);
// after
const traceId = allEvents[0].traceId;
compilePetTelemetry(allEvents.filter(e => e.traceId === traceId));
Defensive patterns

Strategy: validation

Validate before calling

const ids = new Set(events.map(e => e.traceId));
if (ids.size > 1) throw new Error('Multiple traces: ' + [...ids].join(','));

Try / catch

try { compilePetTelemetry(events); } catch (e) { if (e.message === 'Select one trace for the pet.') promptUserToSelectTrace(events); }

Prevention

When it happens

Trigger: Calling compilePetTelemetry with events from two or more different traceId values, even after dedup by keyOf.

Common situations: Concatenating traces from a batch export; a filter accidentally keeping events from sibling traces; importing a whole session instead of one selected trace.

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/22af6b27a1e24b78. Report an issue: GitHub.

Appendix: source

Thrown at pet/src/core/pet-telemetry.ts:85

/** 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.attributes
        : { ...e.attributes, 'whalesong.error_onset_ms': errorOnsetOf(e) - originMs } }))
    .sort((a, b) => a.startTime - b.startTime || order(a.id, b.id));
  let lastOnset = 0;
  for (const e of events) { durationMs = Math.max(durationMs, e.endTime); lastOnset = Math.max(lastOnset, e.startTime); }
  const failures = events.filter(e => e.category === 'error' || e.status === 'error').map(errorOnsetOf).sort((a, b) => a - b);
  if (failures.length) lastOnset = Math.max(lastOnset, failures[failures.length - 1]);
  const count = Math.max(1, Math.ceil(durationMs / PET_BIN_MS), Math.floor(lastOnset / PET_BIN_MS) + 1);
  if (count - firstSequence > 216_000 || count > PET_MAX_SECONDS * 2.5) throw new Error('Pet replay exceeds 24 hours; select a shorter trace.');
  const index = new IntervalIndex(events), result: PetBucket[] = [];
  const recent: WhaleEvent[] = [], names = new Map<string, number>();
  let next = 0, expired = 0, nextFailure = 0;
  for (let sequence = firstSequence; sequence < count; sequence++) {
    const start = sequence * PET_BIN_MS, end = start + PET_BIN_MS;

View on GitHub (pinned to 433685b202)