Hmbown/CodeWhale · error · Error
Invalid interval for
Error message
Invalid interval for ${e.id}. What it means
While binning events into the pyramid, buildPyramid() validates each WhaleEvent's interval. It throws this per-event error when startTime or endTime is not finite, startTime is negative, or endTime is earlier than startTime — an interval that cannot be placed on the time axis.
Solutions
- Fix the offending event's timestamps so 0 <= startTime <= endTime and both are finite; the error message names the event id to locate it.
- Validate/sanitize events before calling buildPyramid, dropping or repairing records with bad intervals.
- For in-flight spans, use a sentinel end equal to startTime or the current time, not a negative or undefined value.
- Check whether the import layer (OTLP/JSONL adapter) is leaving timestamps undefined.
Example fix
// before
buildPyramid([{ id: 'a', startTime: 100, endTime: 50, category: 'tool' }]);
// after
buildPyramid([{ id: 'a', startTime: 50, endTime: 100, category: 'tool' }]); Defensive patterns
Strategy: validation
Validate before calling
const bad = events.filter(e => !Number.isFinite(e.startTime) || !Number.isFinite(e.endTime) || e.startTime < 0 || e.endTime < e.startTime); if (bad.length) repairOrDrop(bad); // before buildPyramid
Type guard
function hasValidInterval(e: WhaleEvent): boolean { return Number.isFinite(e.startTime) && Number.isFinite(e.endTime) && e.startTime >= 0 && e.endTime >= e.startTime; } Try / catch
try { buildPyramid(events, duration); } catch (e) { const m = /Invalid interval for (.+)\./.exec(e.message); if (m) { dropEvent(m[1]); buildPyramid(events, duration); } else throw e; } Prevention
- Sanitize timestamps at the import boundary, not at render time.
- Give in-flight spans endTime = startTime until they close.
- Watch for swapped start/end arguments and clock skew in distributed producers.
When it happens
Trigger: An event in the array has NaN/Infinity startTime or endTime (missing timestamps, null coerced via arithmetic), a negative startTime, or endTime < startTime (swapped/reversed timestamps, e.g. end recorded before start due to clock skew or a logic bug).
Common situations: Traces imported from external formats (OTLP, JSONL) with missing end times; events still in flight recorded with a placeholder end of -1 or undefined; swapping startTime/endTime arguments at the call site; clock skew between distributed producers.
Understand the failure class
Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.
Related errors
- Invalid Engine pet clock.
- Invalid interval for
- Invalid Runtime retention horizon.
- Signal duration must be finite and nonnegative.
- Unknown category for
AI-assisted analysis of Hmbown/CodeWhale@433685b202 (2026-09-15).
Data as JSON: /api/errors/8035fb6d5b538f99.
Report an issue: GitHub.
Appendix: source
Thrown at pet/src/core/signal.ts:21
function emptyLevel(length: number, binMs: number): BinLevel {
const n = CATEGORIES.length * length;
return { binMs, length, onsets: new Float64Array(n), activeMs: new Float64Array(n),
outputTokens: new Float64Array(n), cost: new Float64Array(n), errors: new Float64Array(n), peak: new Float64Array(n) };
}
const FIELDS = ['onsets', 'activeMs', 'outputTokens', 'cost', 'errors'] as const;
/** O(events + channels × bins), including long intervals. No span-length inner loop. */
export function buildPyramid(events: readonly WhaleEvent[], requestedDuration?: number, maxBins = 16_384): SignalPyramid {
if (!Number.isInteger(maxBins) || maxBins < 16 || maxBins > 1_048_576) throw new Error('maxBins must be an integer in [16, 1048576].');
let duration = requestedDuration ?? 1;
for (const e of events) duration = Math.max(duration, e.endTime, e.startTime, e.status === 'error' ? errorOnsetOf(e) : 0);
if (!Number.isFinite(duration) || duration < 0) throw new Error('Signal duration must be finite and nonnegative.');
duration = Math.max(1, duration);
const binMs = 2 ** Math.ceil(Math.log2(Math.max(1, duration / (maxBins - 1))));
const length = Math.floor(duration / binMs) + 1, fine = emptyLevel(length, binMs);
const stride = length + 1;
const activeDiff = new Float64Array(CATEGORIES.length * stride), tokenDiff = new Float64Array(CATEGORIES.length * stride);
for (const e of events) {
if (!Number.isFinite(e.startTime) || !Number.isFinite(e.endTime) || e.startTime < 0 || e.endTime < e.startTime) throw new Error(`Invalid interval for ${e.id}.`);
const channel = CATEGORIES.indexOf(e.category);
if (channel < 0) throw new Error(`Unknown category for ${e.id}.`);
const a = Math.floor(e.startTime / binMs), b = Math.floor(e.endTime / binMs);
const at = channel * length, diff = channel * stride;
fine.onsets[at + a]++;
fine.cost[at + a] += e.cost ?? 0;
if (e.status === 'error') fine.errors[at + Math.floor(errorOnsetOf(e) / binMs)]++;
const d = e.endTime - e.startTime, tokens = e.outputTokens ?? 0;
if (d === 0) { fine.outputTokens[at + a] += tokens; continue; }
if (a === b) { fine.activeMs[at + a] += d; fine.outputTokens[at + a] += tokens; }
else {
const left = (a + 1) * binMs - e.startTime, right = e.endTime - b * binMs, tokenRate = tokens / d;
fine.activeMs[at + a] += left;
fine.activeMs[at + b] += right;
fine.outputTokens[at + a] += left * tokenRate;
fine.outputTokens[at + b] += right * tokenRate;
if (b > a + 1) {
activeDiff[diff + a + 1] += binMs; activeDiff[diff + b] -= binMs;View on GitHub (pinned to 433685b202)