Hmbown/CodeWhale · error · Error
must be nonnegative.
Error message
${field} must be nonnegative. What it means
Numeric field guard in the pet ingest normalizer: the field named by ${field} (e.g. startTime/endTime or a duration) is negative where the normalized event schema requires a nonnegative value. Times may only be negative transiently before the importer rebases relative timestamps against the trace origin; once normalized, they must be >= 0.
Solutions
- Clamp or reject negative values at the producer before ingest
- Recompute the value as Math.max(0, end-start)
- Confirm field ordering — endTime must not precede startTime
Example fix
// before duration: endTime - startTime // can be negative // after duration: Math.max(0, endTime - startTime)
Defensive patterns
Strategy: validation
Validate before calling
if (typeof rec.duration==='number' && rec.duration<0) throw new Error('duration must be nonnegative'); Type guard
const isNonnegative=(v:unknown): v is number => typeof v==='number'&&Number.isFinite(v)&&v>=0;
Try / catch
try { ingest(records); } catch (e) { if (/must be nonnegative/.test(e.message)) { /* clamp with Math.max(0,n) or fix the producer */ } else throw e; } Prevention
- Clamp derived durations to >= 0 at computation time
- Fix clock-skew sources (NTP) so end>=start
- Avoid -1 sentinels in numeric fields
When it happens
Trigger: Calling the ingest normalizer with a negative value for a nonnegative-validated field (e.g. duration, elapsed, sequence offset) via normalizedEvent.
Common situations: Clock skew producing negative durations; subtracting timestamps in the wrong order; test fixtures with -1 sentinels.
Understand the failure class
Background: "value must be between 0 and 1" / "out of range" / "must not be negative" errors: fixing range-validation failures across open-source libraries — this error's family across 42 libraries.
Related errors
- Invalid : expected a finite, safely representable number.
- Missing . Times must be numeric milliseconds.
- Duplicate event identity
- File exceeds the 64 MiB MVP import limit. Split the export…
- maxTraces must be in [1, 64].
AI-assisted analysis of Hmbown/CodeWhale@433685b202 (2026-09-15).
Data as JSON: /api/errors/69850524a09a1731.
Report an issue: GitHub.
Appendix: source
Thrown at pet/src/core/ingest.ts:72
const { payload: _payload, raw: _raw, ...rest } = e;
return { ...rest, links: e.links?.map(link=>({traceId:link.traceId,spanId:link.spanId})), attributes: Object.fromEntries(Object.entries(e.attributes).filter(([k, v]) =>
SAFE_META.test(k) && (typeof v !== 'object' || v === null)
|| ['whalesong.container', 'codewhale.container', 'whalesong.waiting'].includes(k) && typeof v === 'boolean'
// Relative timestamps can be negative before the importer rebases them.
|| k === 'whalesong.error_onset_ms' && typeof v === 'number' && Number.isFinite(v))) };
}
return e;
}
function number(v: unknown, field: string, optional = true): number | undefined {
if (v === undefined || v === null) {
if (optional) return undefined;
throw new Error(`Missing ${field}. Times must be numeric milliseconds.`);
}
if (typeof v !== 'number' || !Number.isFinite(v) || Math.abs(v) > Number.MAX_SAFE_INTEGER) throw new Error(`Invalid ${field}: expected a finite, safely representable number.`);
return v;
}
function nonnegative(v: unknown, field: string): number | undefined {
const n = number(v, field); if (n !== undefined && n < 0) throw new Error(`${field} must be nonnegative.`); return n;
}
function numericAttr(v: unknown): number | undefined {
if (v === undefined || v === null || v === '') return undefined;
const n = Number(v); return Number.isFinite(n) && n >= 0 ? n : undefined;
}
export function categoryFor(name: string, a: Obj): Category {
const explicit = a['whalesong.category'] ?? a.category;
if (CATEGORIES.includes(explicit)) return explicit;
const n = name.toLowerCase(), op = String(a['gen_ai.operation.name'] ?? '').toLowerCase();
if (/exception|^error\b/.test(n)) return 'error';
if (/spawn|fork|subagent/.test(n) || op === 'invoke_agent') return 'agent';
if (/message\.send|handoff|agent\.message/.test(n)) return 'communication';
if (/retrieve|retrieval|context|embedding|vector|memory|rag/.test(n)) return 'memory';
if (/browser|navigate|screenshot|click|playwright/.test(n)) return 'browser';
if (/read_file|write_file|list_dir|file\.|filesystem|fs\.|patch/.test(n)) return 'filesystem';
if (/exec|shell|run_test|cargo|pytest|compile/.test(n)) return 'code';
if (a['gen_ai.request.model'] || a['llm.model_name'] || /reason|completion|generate|chat|llm/.test(n) || ['chat', 'generate_content', 'text_completion'].includes(op)) return 'reasoning';
if (a['gen_ai.tool.name'] || a['tool.name'] || /tool|search|function/.test(n)) return 'tool';View on GitHub (pinned to 433685b202)