Hmbown/CodeWhale · error · Error
Invalid interval for
Error message
Invalid interval for ${e.id}. What it means
buildPyramid() re-validates each event's interval as it bins it: startTime and endTime must be finite numbers, startTime >= 0, and endTime >= startTime. It throws 'Invalid interval for <id>.' naming the offending event when any of these fail. This is a per-event guard distinct from the aggregate duration check.
Solutions
- Find the event by the id in the message and inspect its startTime/endTime values.
- Fix or drop the offending event: swap reversed fields, clamp negatives to 0, or assign a finite endTime to open spans.
- Run the data through importTrace() which enforces the event-v1 interval invariants upstream.
- Add a pre-pass filter that removes or repairs events failing Number.isFinite(t) && t >= 0 && end >= start.
Example fix
// before buildPyramid(events, duration); // throws on one bad event // after const valid = events.filter(e => Number.isFinite(e.startTime) && Number.isFinite(e.endTime) && e.startTime >= 0 && e.endTime >= e.startTime); buildPyramid(valid, duration);
Defensive patterns
Strategy: type-guard
Validate before calling
const validInterval = (e) =>
Number.isFinite(e.startTime) && Number.isFinite(e.endTime) &&
e.startTime >= 0 && e.endTime >= e.startTime;
const bad = events.filter(e => !validInterval(e));
if (bad.length) console.warn('dropping', bad.map(e => e.id));
buildPyramid(events.filter(validInterval), duration); Type guard
const hasValidInterval = (e) => typeof e?.startTime === 'number' && typeof e?.endTime === 'number' && Number.isFinite(e.startTime) && Number.isFinite(e.endTime) && e.startTime >= 0 && e.endTime >= e.startTime;
Try / catch
try {
return buildPyramid(events, duration, maxBins);
} catch (e) {
if (e.message.startsWith('Invalid interval for ')) {
const badId = e.message.slice('Invalid interval for '.length).replace(/\.$/, '');
return buildPyramid(events.filter(ev => ev.id !== badId), duration, maxBins);
}
throw e;
} Prevention
- Sanitize timestamps at import: coerce, clamp negatives to 0, repair swapped start/end
- Drop or quarantine events failing interval invariants before analysis
- Watch for Infinity endTime from unclosed spans — close them with a finite value
When it happens
Trigger: An event with NaN/undefined startTime or endTime; negative timestamps (pre-epoch or a subtracted origin larger than the timestamp); endTime earlier than startTime due to clock skew or reversed start/end fields; Infinity from unclosed spans.
Common situations: Corrupt or partially imported traces; events hand-constructed with (end, start) swapped; timezone/clock adjustments applied to one field but not the other; older data format missing timestamp fields.
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
- supabase-bad-row
- World requires contiguous version 1 pet buckets.
- 1
- A pinned task provider requires an explicit model
- A positive pull request number is required
AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22).
Data as JSON: /api/errors/a1e1a66eb90beb1a.
Report an issue: GitHub.
Appendix: source
Thrown at crates/tui/src/tui/pet_watch/pet-native.js:1326
}
const FIELDS = ['onsets', 'activeMs', 'outputTokens', 'cost', 'errors'];
/** O(events + channels × bins), including long intervals. No span-length inner loop. */
function buildPyramid(events, requestedDuration, maxBins = 16_384) {
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' ? (0, model_js_1.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(model_js_1.CATEGORIES.length * stride), tokenDiff = new Float64Array(model_js_1.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 = model_js_1.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((0, model_js_1.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;
}View on GitHub (pinned to 73e0f67d83)