{"record":{"id":"2a10e707e07f1d1c","repo":"Hmbown/CodeWhale","slug":"signal-duration-must-be-finite-and-nonnegative","errorCode":null,"errorMessage":"Signal duration must be finite and nonnegative.","messagePattern":"Signal duration must be finite and nonnegative\\.","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"crates/tui/src/tui/pet_watch/pet-native.js","lineNumber":1318,"sourceCode":"exports.autocorrelation = autocorrelation;\nexports.periodogram = periodogram;\nexports.unionDuration = unionDuration;\nconst model_js_1 = require(\"./model.js\");\nfunction emptyLevel(length, binMs) {\n    const n = model_js_1.CATEGORIES.length * length;\n    return { binMs, length, onsets: new Float64Array(n), activeMs: new Float64Array(n),\n        outputTokens: new Float64Array(n), cost: new Float64Array(n), errors: new Float64Array(n), peak: new Float64Array(n) };\n}\nconst FIELDS = ['onsets', 'activeMs', 'outputTokens', 'cost', 'errors'];\n/** O(events + channels × bins), including long intervals. No span-length inner loop. */\nfunction buildPyramid(events, requestedDuration, maxBins = 16_384) {\n    if (!Number.isInteger(maxBins) || maxBins < 16 || maxBins > 1_048_576)\n        throw new Error('maxBins must be an integer in [16, 1048576].');\n    let duration = requestedDuration ?? 1;\n    for (const e of events)\n        duration = Math.max(duration, e.endTime, e.startTime, e.status === 'error' ? (0, model_js_1.errorOnsetOf)(e) : 0);\n    if (!Number.isFinite(duration) || duration < 0)\n        throw new Error('Signal duration must be finite and nonnegative.');\n    duration = Math.max(1, duration);\n    const binMs = 2 ** Math.ceil(Math.log2(Math.max(1, duration / (maxBins - 1))));\n    const length = Math.floor(duration / binMs) + 1, fine = emptyLevel(length, binMs);\n    const stride = length + 1;\n    const activeDiff = new Float64Array(model_js_1.CATEGORIES.length * stride), tokenDiff = new Float64Array(model_js_1.CATEGORIES.length * stride);\n    for (const e of events) {\n        if (!Number.isFinite(e.startTime) || !Number.isFinite(e.endTime) || e.startTime < 0 || e.endTime < e.startTime)\n            throw new Error(`Invalid interval for ${e.id}.`);\n        const channel = model_js_1.CATEGORIES.indexOf(e.category);\n        if (channel < 0)\n            throw new Error(`Unknown category for ${e.id}.`);\n        const a = Math.floor(e.startTime / binMs), b = Math.floor(e.endTime / binMs);\n        const at = channel * length, diff = channel * stride;\n        fine.onsets[at + a]++;\n        fine.cost[at + a] += e.cost ?? 0;\n        if (e.status === 'error')\n            fine.errors[at + Math.floor((0, model_js_1.errorOnsetOf)(e) / binMs)]++;\n        const d = e.endTime - e.startTime, tokens = e.outputTokens ?? 0;","sourceCodeStart":1300,"sourceCodeEnd":1336,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/src/tui/pet_watch/pet-native.js#L1300-L1336","documentation":"buildPyramid() derives the signal duration from the requestedDuration argument and the events' start/end/error-onset times, then requires the result to be a finite, non-negative number before computing bin widths. It throws when the effective duration is NaN, Infinity, or negative — usually because requestedDuration was invalid or some event carries non-finite/negative times.","triggerScenarios":"Passing requestedDuration = NaN/undefined where the events array is also empty or itself contains NaN times; an event with endTime = Infinity (never-closed span leaking through) or negative startTime; Math.max over mixed garbage yielding NaN (e.g. one event has startTime undefined so Math.max returns NaN).","commonSituations":"Open-ended spans stored with endTime = Infinity instead of the openEnded flag; a corrupted import leaving undefined timestamps; passing a negative requested duration computed from clock skew; compiling before importTrace normalization.","solutions":["Validate every event's startTime/endTime are finite and non-negative before calling buildPyramid (see its own 'Invalid interval' guard).","Pass an explicit valid requestedDuration instead of relying on event-derived duration.","Normalize open-ended spans to finite endTimes (e.g. use startTime or a cap) before compiling.","Re-run importTrace() on raw data to guarantee event-v1 shapes with finite times."],"exampleFix":"// before\nbuildPyramid(events, requestedDuration); // requestedDuration = NaN\n// after\nconst duration = Number.isFinite(requestedDuration) && requestedDuration >= 0\n  ? requestedDuration\n  : Math.max(0, ...events.map(e => e.endTime));\nbuildPyramid(events, duration);","handlingStrategy":"type-guard","validationCode":"const finiteNonNeg = (n) => typeof n === 'number' && Number.isFinite(n) && n >= 0;\nif (!finiteNonNeg(requestedDuration)) {\n  requestedDuration = Math.max(0, ...events.map(e => e.endTime ?? 0));\n}","typeGuard":"const hasFiniteTimes = (e) =>\n  Number.isFinite(e.startTime) && Number.isFinite(e.endTime) &&\n  e.startTime >= 0 && e.endTime >= e.startTime;","tryCatchPattern":"try {\n  return buildPyramid(events, requestedDuration, maxBins);\n} catch (e) {\n  if (e.message === 'Signal duration must be finite and nonnegative.') {\n    return buildPyramid(events, 1, maxBins);\n  }\n  throw e;\n}","preventionTips":["Never represent open-ended spans with endTime = Infinity — use finite caps","Run all data through importTrace so timestamps are normalized","Guard against clock skew producing negative durations when subtracting origins"],"tags":["validation","duration","javascript"],"backgroundTag":"invalid-argument-value","analyzedSha":"73e0f67d83c59909b571efdfc88c4bc28c309cb1","analyzedAt":"2026-09-22T01:30:00.501Z","contentChangedAt":"2026-09-22T01:30:00.501Z","schemaVersion":2},"datasetVersion":"2026-09-22T16:17:23.217Z"}