Hmbown/CodeWhale · error · Error

Record requires nonempty id, traceId, and name.

Error message

Record ${index + 1} requires nonempty id, traceId, and name.

What it means

normalizedEvent requires every ingested record to carry nonempty id, traceId, and name strings (via str(), which coerces and empties non-strings). If any is empty/missing, pet/src/core/ingest.ts:126 throws with the 1-based record index.

Solutions

  1. Populate id, traceId, and name with nonempty strings on every record
  2. Skip or repair records missing identity before calling the ingest API
  3. Check the mapping layer for fields renamed or dropped during conversion

Example fix

// before
{id:'', traceId:'t1', name:'db.query'}
// after
{id:'span-123', traceId:'t1', name:'db.query'}
Defensive patterns

Strategy: validation

Validate before calling

if (!rec.id||!rec.traceId||!rec.name) throw new Error('record missing id/traceId/name before ingest');

Type guard

const hasIdentity=(r:any): r is {id:string;traceId:string;name:string} => typeof r?.id==='string'&&r.id.length>0&&typeof r?.traceId==='string'&&r.traceId.length>0&&typeof r?.name==='string'&&r.name.length>0;

Try / catch

try { ingest(records); } catch (e) { if (/requires nonempty id, traceId, and name/.test(e.message)) { /* quarantine the bad record; the message names the index */ } else throw e; }

Prevention

When it happens

Trigger: Ingesting an OTLP trace where a span lacks spanId, traceId, or name — or those fields are non-strings (null/numbers) that coerce to ''.

Common situations: Spans exported from instrumentation that omits names on synthetic events; joins with null ids from upstream storage; partially written records from a crashing exporter.

Understand the failure class

Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@433685b202 (2026-09-15). Data as JSON: /api/errors/0ff27cfce1570e87. Report an issue: GitHub.

Appendix: source

Thrown at pet/src/core/ingest.ts:126

  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 {
    schemaVersion: 1, id, traceId, name, parentId: str(a.parentId), startTime, endTime,
    openEnded: a.endTime === undefined || a.openEnded === true,
    agentId: str(a.agentId) ?? 'unattributed', agentType: str(a.agentType), parentAgentId: str(a.parentAgentId),
    category: a.category ?? categoryFor(name, at), subtype: str(a.subtype),
    model: str(a.model), provider: str(a.provider), tool: str(a.tool),
    inputTokens: nonnegative(a.inputTokens, 'inputTokens'), outputTokens: nonnegative(a.outputTokens, 'outputTokens'),
    cachedTokens: nonnegative(a.cachedTokens, 'cachedTokens'), cost: nonnegative(a.cost, 'cost'),
    costCurrency: str(a.costCurrency), latency: nonnegative(a.latency, 'latency'),
    contextTokens: nonnegative(a.contextTokens, 'contextTokens'), contextLimit: nonnegative(a.contextLimit, 'contextLimit'),

View on GitHub (pinned to 433685b202)