{"record":{"id":"c6edaff353f2fe1a","repo":"Hmbown/CodeWhale","slug":"invalid-first-pet-bucket-telemetry","errorCode":null,"errorMessage":"Invalid first pet bucket.","messagePattern":"Invalid first pet bucket\\.","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"pet/src/core/pet-telemetry.ts","lineNumber":73,"sourceCode":"    catch (error) { this.reset(); throw error; }\n    const previous = this.sequence; this.sequence = packet.sequence;\n    if (previous === undefined || packet.sequence <= previous) return;\n    return packet;\n  }\n}\n\nconst order = (a: string, b: string) => a < b ? -1 : a > b ? 1 : 0;\nconst keyOf = (e: WhaleEvent) => JSON.stringify([e.traceId, e.id]);\nconst isContainer = (e: WhaleEvent) => e.attributes['whalesong.container'] === true\n  || e.attributes['codewhale.container'] === true;\n\n/** Compile a single trace. Unknown-duration spans provide onsets, not occupancy.\n * Updates of the same trace/id replace earlier snapshots rather than double count.\n * An endpoint onset gets its own bucket; intervals use [start, end). */\nexport function compilePetTelemetry(input: readonly WhaleEvent[], durationMs = 0, firstSequence = 0, originMs = 0): PetBucket[] {\n  if (input.length > 250_000) throw new Error('Pet input exceeds 250000 events.');\n  if (!Number.isFinite(durationMs) || durationMs < 0) throw new Error('Invalid pet duration.');\n  if (!Number.isSafeInteger(firstSequence) || firstSequence < 0) throw new Error('Invalid first pet bucket.');\n  if (!Number.isFinite(originMs)) throw new Error('Invalid pet clock origin.');\n  durationMs = Math.max(0, durationMs - originMs);\n  const unique = new Map<string, WhaleEvent>();\n  const traces = new Set<string>();\n  for (const e of input) {\n    if (e.schemaVersion !== 1 || !e.id || !e.traceId || !CATEGORIES.includes(e.category)\n      || !Number.isFinite(e.startTime) || !Number.isFinite(e.endTime)\n      || e.startTime < 0 || e.endTime < e.startTime || !e.attributes)\n      throw new Error('Invalid event-v1 pet input. Import through importTrace first.');\n    traces.add(e.traceId); unique.set(keyOf(e), e);\n  }\n  if (traces.size > 1) throw new Error('Select one trace for the pet.');\n  const parents = new Set([...unique.values()].filter(e => e.parentId).map(e => e.parentId!));\n  const events = [...unique.values()].filter(e => !isContainer(e)\n    && !(e.category === 'orchestration' && parents.has(e.id)))\n    .map(e => ({ ...e, startTime: e.startTime - originMs, endTime: (e.openEnded ? e.startTime : e.endTime) - originMs,\n      attributes: e.attributes['whalesong.error_onset_ms'] === undefined ? e.attributes\n        : { ...e.attributes, 'whalesong.error_onset_ms': errorOnsetOf(e) - originMs } }))","sourceCodeStart":55,"sourceCodeEnd":91,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/433685b2024e7bc4c99e1e2e326bcad39b4d9d65/pet/src/core/pet-telemetry.ts#L55-L91","documentation":"compilePetTelemetry requires firstSequence to be a safe non-negative integer, because it is used directly as the starting bucket sequence number. Floats, negatives, or numbers beyond Number.MAX_SAFE_INTEGER throw this error.","triggerScenarios":"Calling compilePetTelemetry with firstSequence = -1, a fractional value from division, NaN, or an unbounded computed offset (e.g. ms-since-epoch divided by PET_BIN_MS without flooring/clamping).","commonSituations":"Computing the first bucket as Date.now()/PET_BIN_MS and getting a float; an uninitialized variable defaulting to NaN; a negative offset from a clock-origin subtraction gone wrong.","solutions":["Floor and validate: Math.floor(firstSequence), and clamp at 0 before calling","Pass the explicit default 0 when starting a fresh tape","Compute sequence numbers with Math.floor(originMs / PET_BIN_MS) and assert Number.isSafeInteger","Skip compilation when the computed first sequence is invalid and log the cause"],"exampleFix":"// before\ncompilePetTelemetry(events, duration, elapsedMs / PET_BIN_MS);\n// after\nconst first = Math.max(0, Math.floor(elapsedMs / PET_BIN_MS));\ncompilePetTelemetry(events, duration, Number.isSafeInteger(first) ? first : 0);","handlingStrategy":"validation","validationCode":"const first = Math.max(0, Math.floor(elapsedMs / PET_BIN_MS));\nif (!Number.isSafeInteger(first)) throw new Error('bad firstSequence before compile');","typeGuard":"function isValidFirstSequence(n: unknown): n is number {\n  return typeof n === 'number' && Number.isSafeInteger(n) && n >= 0;\n}","tryCatchPattern":"try {\n  compilePetTelemetry(events, duration, firstSequence);\n} catch (e) {\n  if (e.message === 'Invalid first pet bucket.') {\n    compilePetTelemetry(events, duration, 0); // restart tape numbering\n  } else throw e;\n}","preventionTips":["Always Math.floor computed bucket indices","Clamp negatives to 0","Assert Number.isSafeInteger before passing sequence offsets"],"tags":["validation","integer","telemetry"],"backgroundTag":"invalid-argument-value","analyzedSha":"433685b2024e7bc4c99e1e2e326bcad39b4d9d65","analyzedAt":"2026-09-15T12:24:24.634Z","contentChangedAt":"2026-09-15T12:24:24.634Z","schemaVersion":2},"datasetVersion":"2026-09-22T16:17:23.217Z"}