Hmbown/CodeWhale · error · Error
Invalid failure observation time.
Error message
Invalid failure observation time.
What it means
errorOnsetOf reads the whalesong.error_onset_ms attribute recording when a failure was observed on an event. The attribute is valid only if it is a finite number greater than or equal to the event's startTime; anything else (missing type, NaN/Infinity, or a value before the span began) throws, keeping failure timing trustworthy across all signal views.
Solutions
- Fix the producer to emit a finite number >= the event's startTime, or omit the attribute entirely (it then defaults to startTime).
- Clamp or drop bad attribute values before ingestion: if onset < startTime, set it equal to startTime or delete the key.
- Verify the attribute is not stringified during export/import (JSON numbers must stay numbers).
- Check for clock skew between the component writing onsets and the one starting spans; normalize clocks before stamping.
Example fix
// before
{ "attributes": { "whalesong.error_onset_ms": "1700000000000" } } // string
// after
{ "attributes": { "whalesong.error_onset_ms": 1700000000000 } }
Defensive patterns
Strategy: type-guard
Validate before calling
const onset = e.attributes['whalesong.error_onset_ms'];
if (onset !== undefined && (typeof onset !== 'number' || !Number.isFinite(onset) || onset < e.startTime)) {
delete e.attributes['whalesong.error_onset_ms']; // or clamp to e.startTime
} Type guard
function hasValidOnset(e: WhaleEvent): boolean {
const t = e.attributes['whalesong.error_onset_ms'];
return t === undefined || (typeof t === 'number' && Number.isFinite(t) && t >= e.startTime);
} Try / catch
try {
const onset = errorOnsetOf(event);
} catch (e) {
if (e instanceof Error && e.message === 'Invalid failure observation time.') {
console.warn('Bad error_onset_ms; falling back to event startTime.');
const onset = event.startTime;
}
} Prevention
- Only emit whalesong.error_onset_ms as a JSON number >= the span's startTime.
- Omit the attribute when the onset is unknown — it then defaults to startTime.
- Normalize clock skew between the failure recorder and the span producer.
- Never stringify the attribute during serialization; guard against schema drift in producers.
When it happens
Trigger: An event carries whalesong.error_onset_ms that is a string, NaN, Infinity, or a number smaller than e.startTime, and errorOnsetOf is invoked by prune, normalized, snapshot, importTrace, events, or buildPyramid.
Common situations: A producer emitting the attribute before computing the span start; serializing the attribute to a string; clock skew between the failure recorder and span starter; hand-edited bundles setting negative or bogus onsets.
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
- GitHub timestamp is invalid
- Invalid failure observation time.
- Record : endTime precedes startTime.
- 1
- A pinned task provider requires an explicit model
AI-assisted analysis of Hmbown/CodeWhale@433685b202 (2026-09-15).
Data as JSON: /api/errors/2177ffc21a2703bd.
Report an issue: GitHub.
Appendix: source
Thrown at pet/src/core/model.ts:88
if (f.model && e.model !== f.model) return false;
if (f.tool && e.tool !== f.tool) return false;
if (f.status && e.status !== f.status) return false;
if (f.query) {
const q = f.query.toLowerCase();
// Search is deliberately content-aware but runs only over locally retained fields.
if (![e.name, e.id, e.agentId, e.model, e.tool, e.provider, e.category,
JSON.stringify(e.attributes), JSON.stringify(e.payload), JSON.stringify(e.raw)].filter(Boolean).join(' ').toLowerCase().includes(q)) return false;
}
return true;
}
export const durationOf = (e: WhaleEvent): number => Math.max(0, e.endTime - e.startTime);
/** An explicit failure receipt can arrive after a span began or ended. Keep
* its timestamp distinct from the operation onset in every signal view. */
export function errorOnsetOf(e: WhaleEvent): number {
const time = e.attributes['whalesong.error_onset_ms'];
if (time === undefined) return e.startTime;
if (typeof time !== 'number' || !Number.isFinite(time) || time < e.startTime)
throw new Error('Invalid failure observation time.');
return time;
}
export function stableHash(text: string, seed = 2166136261): number {
let h = seed;
for (let i = 0; i < text.length; i++) { h ^= text.charCodeAt(i); h = Math.imul(h, 16777619); }
return h >>> 0;
}
export function quantile(a: number[], q: number): number {
if (!a.length) return 0;
const s = [...a].sort((a, b) => a - b), x = Math.min(1, Math.max(0, q)) * (s.length - 1);
return s[Math.floor(x)] + (s[Math.ceil(x)] - s[Math.floor(x)]) * (x % 1);
}
export function clamp(x: number, lo: number, hi: number): number { return Math.max(lo, Math.min(hi, x)); }
export function formatTime(ms: number, precise = false): string {
const m = Math.floor(Math.max(0, ms) / 60000), s = Math.floor(Math.max(0, ms) / 1000) % 60;
return `${String(m).padStart(2, '0')}:${String(s).padStart(2, '0')}${precise ? '.' + String(Math.floor(ms % 1000)).padStart(3, '0') : ''}`;
}
View on GitHub (pinned to 433685b202)