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

  1. Supply a plain decimal integer string within uint64 range
  2. Convert ISO dates to unix nanoseconds before ingest
  3. 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

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


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)