Hmbown/CodeWhale · error · Error

Invalid failure observation time.

Error message

Invalid failure observation time.

What it means

errorOnsetOf() extracts the failure observation timestamp from a telemetry event's 'whalesong.error_onset_ms' attribute, falling back to startTime when absent. When the attribute is present it must be a finite number not earlier than the event's startTime; otherwise the observation time is inconsistent with the event itself and would produce a negative-duration signal view. The library throws to keep error onsets anchored to a valid timeline position.

Solutions

  1. Fix the producer to emit whalesong.error_onset_ms as a finite number on the same clock as startTime, with onset >= startTime.
  2. If the attribute cannot be trusted, omit it entirely so errorOnsetOf falls back to e.startTime.
  3. At the ingestion boundary, coerce/validate the attribute (parse strings, drop values < startTime) before the event reaches errorOnsetOf.
  4. Synchronize clocks across the components that write startTime and error_onset_ms.

Example fix

// before
e.attributes['whalesong.error_onset_ms'] = String(Date.now()); // string, different clock
// after
const onset = e.startTime + measuredDelayMs;
e.attributes['whalesong.error_onset_ms'] = Math.max(onset, e.startTime); // number, same clock
Defensive patterns

Strategy: validation

Validate before calling

function safeErrorOnset(e) {
  const t = e.attributes?.['whalesong.error_onset_ms'];
  if (t === undefined) return e.startTime;
  const n = Number(t);
  return (typeof n === 'number' && Number.isFinite(n) && n >= e.startTime) ? n : e.startTime;
}

Type guard

const hasValidOnset = (e) => e.attributes?.['whalesong.error_onset_ms'] === undefined || (typeof e.attributes['whalesong.error_onset_ms'] === 'number' && Number.isFinite(e.attributes['whalesong.error_onset_ms']) && e.attributes['whalesong.error_onset_ms'] >= e.startTime);

Try / catch

try {
  const onset = errorOnsetOf(e);
} catch (err) {
  if (err.message === 'Invalid failure observation time.') {
    onset = e.startTime; // fall back to event start
  } else throw err;
}

Prevention

When it happens

Trigger: Processing a telemetry event whose attributes['whalesong.error_onset_ms'] is a non-number (string from JSON logs), NaN/Infinity, or a value smaller than e.startTime — e.g. an onset stamped with a different clock or recorded before the event began.

Common situations: Log exporters serializing the attribute as a string; clock skew between the component writing onset_ms and the one writing startTime; a producer using epoch ms while startTime uses a monotonic or relative clock; hand-crafted test events.

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


AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22). Data as JSON: /api/errors/ba620b2a778ae8ba. Report an issue: GitHub.

Appendix: source

Thrown at crates/tui/src/tui/pet_watch/pet-native.js:625

    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;
}
const durationOf = (e) => Math.max(0, e.endTime - e.startTime);
exports.durationOf = durationOf;
/** An explicit failure receipt can arrive after a span began or ended. Keep
 * its timestamp distinct from the operation onset in every signal view. */
function errorOnsetOf(e) {
    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;
}
function stableHash(text, seed = 2166136261) {
    let h = seed;
    for (let i = 0; i < text.length; i++) {
        h ^= text.charCodeAt(i);
        h = Math.imul(h, 16777619);
    }
    return h >>> 0;
}
function quantile(a, q) {
    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);
}
function clamp(x, lo, hi) { return Math.max(lo, Math.min(hi, x)); }
function formatTime(ms, precise = false) {

View on GitHub (pinned to 73e0f67d83)