{"record":{"id":"0ff27cfce1570e87","repo":"Hmbown/CodeWhale","slug":"record-index-1-requires-nonempty-id-traceid-and-name","errorCode":null,"errorMessage":"Record ${index + 1} requires nonempty id, traceId, and name.","messagePattern":"Record (.+?) requires nonempty id, traceId, and name\\.","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"pet/src/core/ingest.ts","lineNumber":126,"sourceCode":"  return v;\n}\nexport function attributes(value: unknown): Obj {\n  if (!Array.isArray(value)) return obj(value);\n  return Object.fromEntries(value.filter(x => typeof x?.key === 'string').map(x => [x.key, decodeAnyValue(x.value)]));\n}\nfunction ns(v: unknown, field: string): bigint {\n  if (typeof v === 'number' && !Number.isSafeInteger(v)) throw new Error(`${field} lost precision: encode OTLP nanoseconds as a decimal string.`);\n  const s = String(v ?? '');\n  if (!/^\\d{1,20}$/.test(s) || BigInt(s) > 18446744073709551615n) throw new Error(`Invalid ${field}: expected an OTLP nanosecond integer string.`);\n  return BigInt(s);\n}\nfunction otelStatus(v: unknown): Status {\n  const c = obj(v).code;\n  return c === 2 || c === 'STATUS_CODE_ERROR' ? 'error' : c === 1 || c === 'STATUS_CODE_OK' ? 'success' : 'unknown';\n}\nfunction normalizedEvent(v: unknown, index: number): WhaleEvent {\n  const a = obj(v), id = str(a.id), traceId = str(a.traceId), name = str(a.name);\n  if (!id || !traceId || !name) throw new Error(`Record ${index + 1} requires nonempty id, traceId, and name.`);\n  if (a.schemaVersion !== undefined && a.schemaVersion !== 1) throw new Error(`Record ${index + 1}: unsupported schemaVersion ${a.schemaVersion}.`);\n  const startTime = number(a.startTime, 'startTime', false)!;\n  const endTime = number(a.endTime, 'endTime') ?? startTime;\n  if (endTime < startTime) throw new Error(`Record ${index + 1}: endTime precedes startTime.`);\n  if (a.category !== undefined && !CATEGORIES.includes(a.category)) throw new Error(`Unknown category \"${a.category}\". Use \"other\" plus subtype for extensions.`);\n  const allowed: Status[] = ['pending', 'running', 'success', 'error', 'unknown'];\n  if (a.status !== undefined && !allowed.includes(a.status)) throw new Error(`Record ${index + 1}: invalid status.`);\n  const at = obj(a.attributes);\n  return {\n    schemaVersion: 1, id, traceId, name, parentId: str(a.parentId), startTime, endTime,\n    openEnded: a.endTime === undefined || a.openEnded === true,\n    agentId: str(a.agentId) ?? 'unattributed', agentType: str(a.agentType), parentAgentId: str(a.parentAgentId),\n    category: a.category ?? categoryFor(name, at), subtype: str(a.subtype),\n    model: str(a.model), provider: str(a.provider), tool: str(a.tool),\n    inputTokens: nonnegative(a.inputTokens, 'inputTokens'), outputTokens: nonnegative(a.outputTokens, 'outputTokens'),\n    cachedTokens: nonnegative(a.cachedTokens, 'cachedTokens'), cost: nonnegative(a.cost, 'cost'),\n    costCurrency: str(a.costCurrency), latency: nonnegative(a.latency, 'latency'),\n    contextTokens: nonnegative(a.contextTokens, 'contextTokens'), contextLimit: nonnegative(a.contextLimit, 'contextLimit'),","sourceCodeStart":108,"sourceCodeEnd":144,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/433685b2024e7bc4c99e1e2e326bcad39b4d9d65/pet/src/core/ingest.ts#L108-L144","documentation":"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.","triggerScenarios":"Ingesting an OTLP trace where a span lacks spanId, traceId, or name — or those fields are non-strings (null/numbers) that coerce to ''.","commonSituations":"Spans exported from instrumentation that omits names on synthetic events; joins with null ids from upstream storage; partially written records from a crashing exporter.","solutions":["Populate id, traceId, and name with nonempty strings on every record","Skip or repair records missing identity before calling the ingest API","Check the mapping layer for fields renamed or dropped during conversion"],"exampleFix":"// before\n{id:'', traceId:'t1', name:'db.query'}\n// after\n{id:'span-123', traceId:'t1', name:'db.query'}","handlingStrategy":"validation","validationCode":"if (!rec.id||!rec.traceId||!rec.name) throw new Error('record missing id/traceId/name before ingest');","typeGuard":"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;","tryCatchPattern":"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; }","preventionTips":["Require id/traceId/name in your ingestion schema","Sanitize upstream data that may contain null ids","Generate ids at the producer if the source cannot supply them"],"tags":["validation","identity","missing-field","ingest"],"backgroundTag":"empty-required-field","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"}