Hmbown/CodeWhale · error · Error
Invalid : expected an OTLP nanosecond integer string.
Error message
Invalid ${field}: expected an OTLP nanosecond integer string. What it means
The `ns` helper validates its input as 1-20 ASCII digits whose value fits in uint64 (<= 18446744073709551615) after converting via String(v) (pet/src/core/ingest.ts:117). Anything else — negative numbers, floats, non-numeric strings, values above uint64 max — throws.
Solutions
- Supply a plain decimal integer string within uint64 range
- Convert ISO dates to unix nanoseconds before ingest
- Check for negative or float values from your producer and fix its serialization
Example fix
// before startTimeUnixNano: '2023-11-14T22:13:20Z' // after startTimeUnixNano: '1700000000000000000'
Defensive patterns
Strategy: validation
Validate before calling
const okNs=(v:unknown)=>/^\d{1,20}$/.test(String(v??''))&&BigInt(String(v))<=18446744073709551615n; Type guard
const isUint64String=(v:unknown): v is string => typeof v==='string'&&/^\d{1,20}$/.test(v)&&BigInt(v)<=18446744073709551615n; Try / catch
try { ingest(records); } catch (e) { if (/expected an OTLP nanosecond integer string/.test(e.message)) { /* convert ISO/negative/float values to uint64 decimal strings */ } else throw e; } Prevention
- Validate nanosecond fields against /^\d{1,20}$/ at the boundary
- Convert timestamps to unix nanos once, at the producer
- Guard against signed/float clock values from exporters
When it happens
Trigger: Passing a negative number, decimal string, empty value, or a nanosecond value exceeding 2^64-1 to the ns-validated timestamp fields during OTLP ingest.
Common situations: Timestamps serialized as ISO strings; float nanosecond values from clock math; signed 64-bit values from Go exporters overflowing uint64; empty string for a missing timestamp.
Understand the failure class
Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.
Related errors
- lost precision: encode OTLP nanoseconds as a decimal string.
- OTLP span : end precedes start.
- CRON field contains an empty list item
- custom provider name may only use letters, numbers, '-' and…
- DeepSeek Harness credentials line
AI-assisted analysis of Hmbown/CodeWhale@433685b202 (2026-09-15).
Data as JSON: /api/errors/95fdf9f91b93a5d5.
Report an issue: GitHub.
Appendix: source
Thrown at pet/src/core/ingest.ts:117
if ('boolValue' in a) return a.boolValue;
if ('doubleValue' in a) return a.doubleValue;
if ('intValue' in a) {
const n = Number(a.intValue);
return Number.isSafeInteger(n) ? n : String(a.intValue);
}
if ('bytesValue' in a) return a.bytesValue;
if ('arrayValue' in a) return list(obj(a.arrayValue).values).map(decodeAnyValue);
if ('kvlistValue' in a) return attributes(obj(a.kvlistValue).values);
return v;
}
export function attributes(value: unknown): Obj {
if (!Array.isArray(value)) return obj(value);
return Object.fromEntries(value.filter(x => typeof x?.key === 'string').map(x => [x.key, decodeAnyValue(x.value)]));
}
function ns(v: unknown, field: string): bigint {
if (typeof v === 'number' && !Number.isSafeInteger(v)) throw new Error(`${field} lost precision: encode OTLP nanoseconds as a decimal string.`);
const s = String(v ?? '');
if (!/^\d{1,20}$/.test(s) || BigInt(s) > 18446744073709551615n) throw new Error(`Invalid ${field}: expected an OTLP nanosecond integer string.`);
return BigInt(s);
}
function otelStatus(v: unknown): Status {
const c = obj(v).code;
return c === 2 || c === 'STATUS_CODE_ERROR' ? 'error' : c === 1 || c === 'STATUS_CODE_OK' ? 'success' : 'unknown';
}
function normalizedEvent(v: unknown, index: number): WhaleEvent {
const a = obj(v), id = str(a.id), traceId = str(a.traceId), name = str(a.name);
if (!id || !traceId || !name) throw new Error(`Record ${index + 1} requires nonempty id, traceId, and name.`);
if (a.schemaVersion !== undefined && a.schemaVersion !== 1) throw new Error(`Record ${index + 1}: unsupported schemaVersion ${a.schemaVersion}.`);
const startTime = number(a.startTime, 'startTime', false)!;
const endTime = number(a.endTime, 'endTime') ?? startTime;
if (endTime < startTime) throw new Error(`Record ${index + 1}: endTime precedes startTime.`);
if (a.category !== undefined && !CATEGORIES.includes(a.category)) throw new Error(`Unknown category "${a.category}". Use "other" plus subtype for extensions.`);
const allowed: Status[] = ['pending', 'running', 'success', 'error', 'unknown'];
if (a.status !== undefined && !allowed.includes(a.status)) throw new Error(`Record ${index + 1}: invalid status.`);
const at = obj(a.attributes);
return {View on GitHub (pinned to 433685b202)