{"record":{"id":"954c3da5ce06781a","repo":"Hmbown/CodeWhale","slug":"invalid-failure-observation-time-pet-native","errorCode":null,"errorMessage":"Invalid failure observation time.","messagePattern":"Invalid failure observation time\\.","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"pet/ios/Resources/pet-native.js","lineNumber":625,"sourceCode":"    if (f.query) {\n        const q = f.query.toLowerCase();\n        // Search is deliberately content-aware but runs only over locally retained fields.\n        if (![e.name, e.id, e.agentId, e.model, e.tool, e.provider, e.category,\n            JSON.stringify(e.attributes), JSON.stringify(e.payload), JSON.stringify(e.raw)].filter(Boolean).join(' ').toLowerCase().includes(q))\n            return false;\n    }\n    return true;\n}\nconst durationOf = (e) => Math.max(0, e.endTime - e.startTime);\nexports.durationOf = durationOf;\n/** An explicit failure receipt can arrive after a span began or ended. Keep\n * its timestamp distinct from the operation onset in every signal view. */\nfunction errorOnsetOf(e) {\n    const time = e.attributes['whalesong.error_onset_ms'];\n    if (time === undefined)\n        return e.startTime;\n    if (typeof time !== 'number' || !Number.isFinite(time) || time < e.startTime)\n        throw new Error('Invalid failure observation time.');\n    return time;\n}\nfunction stableHash(text, seed = 2166136261) {\n    let h = seed;\n    for (let i = 0; i < text.length; i++) {\n        h ^= text.charCodeAt(i);\n        h = Math.imul(h, 16777619);\n    }\n    return h >>> 0;\n}\nfunction quantile(a, q) {\n    if (!a.length)\n        return 0;\n    const s = [...a].sort((a, b) => a - b), x = Math.min(1, Math.max(0, q)) * (s.length - 1);\n    return s[Math.floor(x)] + (s[Math.ceil(x)] - s[Math.floor(x)]) * (x % 1);\n}\nfunction clamp(x, lo, hi) { return Math.max(lo, Math.min(hi, x)); }\nfunction formatTime(ms, precise = false) {","sourceCodeStart":607,"sourceCodeEnd":643,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/433685b2024e7bc4c99e1e2e326bcad39b4d9d65/pet/ios/Resources/pet-native.js#L607-L643","documentation":"`errorOnsetOf(e)` resolves the failure observation timestamp from the `whalesong.error_onset_ms` attribute, falling back to `e.startTime`. It throws when the attribute is present but not a finite number, or when it is earlier than the error's start time — the library treats an onset before the error began as corrupt observation data.","triggerScenarios":"Feeding error/telemetry events into the signal-view pipeline where an event carries `whalesong.error_onset_ms` that is a string, NaN, Infinity, or a value smaller than `e.startTime` (e.g. onset set in a different unit, or stamped before the error actually started).","commonSituations":"Producers mixing milliseconds and seconds when writing `error_onset_ms`; replaying old recordings with clock skew; test fixtures hand-writing onset attributes below startTime; JSON round-trips turning the number into a string.","solutions":["Fix the event producer so `whalesong.error_onset_ms` is a finite number of milliseconds that is >= `e.startTime`.","Ensure units are consistent — convert seconds to ms (`seconds * 1000`) before attaching the attribute.","If the onset is unreliable, omit the attribute entirely so the function falls back to `e.startTime` instead of throwing."],"exampleFix":"// before\ne.attributes['whalesong.error_onset_ms'] = onsetSeconds;          // wrong unit\n// after\nconst onsetMs = Math.max(e.startTime, onsetSeconds * 1000);       // ms, >= startTime\ne.attributes['whalesong.error_onset_ms'] = onsetMs;","handlingStrategy":"validation","validationCode":"function isValidOnset(e) { const t = e.attributes?.['whalesong.error_onset_ms']; return t === undefined || (typeof t === 'number' && Number.isFinite(t) && t >= e.startTime); }","typeGuard":"function hasValidOnsetAttr(e) { const t = e.attributes['whalesong.error_onset_ms']; return t === undefined || (typeof t === 'number' && Number.isFinite(t) && t >= e.startTime); }","tryCatchPattern":"try { onset = errorOnsetOf(e); } catch (err) { onset = e.startTime; /* fall back, log producer bug */ }","preventionTips":["Standardize onset units to milliseconds at the producer boundary.","Clamp onset to >= startTime when stamping the attribute.","Omit the attribute rather than writing a dubious value, so the built-in startTime fallback applies."],"tags":["telemetry","timestamp","validation","javascript"],"backgroundTag":"invalid-argument-value","analyzedSha":"433685b2024e7bc4c99e1e2e326bcad39b4d9d65","analyzedAt":"2026-09-15T12:24:24.634Z","contentChangedAt":"2026-09-15T12:24:24.634Z","schemaVersion":2},"datasetVersion":"2026-09-22T11:17:16.035Z"}