{"record":{"id":"2171e76d894fc60e","repo":"Hmbown/CodeWhale","slug":"no-spans-found-in-resourcespans-scopespans-spans","errorCode":null,"errorMessage":"No spans found in resourceSpans[].scopeSpans[].spans[].","messagePattern":"No spans found in resourceSpans\\[\\]\\.scopeSpans\\[\\]\\.spans\\[\\]\\.","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"pet/src/core/ingest.ts","lineNumber":164,"sourceCode":"    sourceId: str(a.sourceId), targetId: str(a.targetId), targetType: str(a.targetType),\n    links: list(a.links).filter(x => typeof x?.traceId === 'string' && typeof x?.spanId === 'string'),\n    attributes: at, payload: a.payload, raw: a.raw ?? v,\n    observation: a.observation === undefined ? undefined : validateObservation(a.observation),\n  };\n}\ninterface OtlpRecord { span: Obj; resource: Obj; scope: Obj; resourceSchema?: string; scopeSchema?: string }\nfunction otlpRecords(doc: Obj): OtlpRecord[] {\n  const out: OtlpRecord[] = [];\n  for (const r of list(doc.resourceSpans)) {\n    for (const s of list(r.scopeSpans ?? r.instrumentationLibrarySpans)) {\n      for (const span of list(s.spans)) out.push({ span: obj(span), resource: obj(r.resource), scope: obj(s.scope ?? s.instrumentationLibrary), resourceSchema: r.schemaUrl, scopeSchema: s.schemaUrl });\n    }\n  }\n  return out;\n}\nfunction fromOTLP(doc: Obj, maxEvents: number): { events: WhaleEvent[]; origins: Map<string, string>; warnings: string[] } {\n  const records = otlpRecords(doc), bases = new Map<string, bigint>(), warnings: string[] = [];\n  if (!records.length) throw new Error('No spans found in resourceSpans[].scopeSpans[].spans[].');\n  if (records.length > maxEvents) throw new Error(`Import exceeds the ${maxEvents.toLocaleString()} event limit.`);\n  for (const { span: s } of records) {\n    if (!str(s.traceId) || !str(s.spanId)) throw new Error('Every OTLP span requires traceId and spanId.');\n    const start = ns(s.startTimeUnixNano, 'startTimeUnixNano');\n    const end = s.endTimeUnixNano === undefined ? start : ns(s.endTimeUnixNano, 'endTimeUnixNano');\n    if (end < start) throw new Error(`OTLP span ${s.spanId}: end precedes start.`);\n    let earliest = start;\n    for (const e of list(s.events)) { const t = ns(e.timeUnixNano, 'event.timeUnixNano'); if (t < earliest) earliest = t; }\n    if (!bases.has(s.traceId) || earliest < bases.get(s.traceId)!) bases.set(s.traceId, earliest);\n    if ((s.droppedEventsCount ?? 0) > 0) warnings.push(`Span ${s.spanId} reports ${s.droppedEventsCount} dropped events; coverage is incomplete.`);\n  }\n  const events: WhaleEvent[] = [];\n  for (const rec of records) {\n    const s = rec.span, a = { ...attributes(rec.resource.attributes), ...attributes(s.attributes) };\n    const origin = bases.get(s.traceId)!, start = ns(s.startTimeUnixNano, 'startTimeUnixNano');\n    const end = s.endTimeUnixNano === undefined ? start : ns(s.endTimeUnixNano, 'endTimeUnixNano');\n    const name = String(s.name ?? 'unnamed span');\n    const e: WhaleEvent = {","sourceCodeStart":146,"sourceCodeEnd":182,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/433685b2024e7bc4c99e1e2e326bcad39b4d9d65/pet/src/core/ingest.ts#L146-L182","documentation":"fromOTLP parses an OTLP JSON trace-export document and expects spans under resourceSpans[].scopeSpans[].spans[]. otlpRecords found zero spans, so there is nothing to import; the library throws instead of returning an empty trace.","triggerScenarios":"Calling the OTLP import path with a document where resourceSpans is missing, empty, scopeSpans is empty, spans arrays are empty, or the JSON is valid but of a different shape (e.g. metrics or logs export, or protobuf-encoded bytes pasted as text).","commonSituations":"Exporting from an OTel collector with the wrong signal configured; passing an OTLP/HTTP protobuf body instead of the JSON format; truncation of the export; pointing the importer at a logs/metrics export file.","solutions":["Verify the file is an OTLP traces JSON export containing resourceSpans[].scopeSpans[].spans[].","Re-export with the correct signal: ensure the collector/SDK is configured to export spans (traces), not metrics/logs.","Check the file isn't empty or truncated; re-run the export with a healthy pipeline.","Ensure you use OTLP JSON encoding, not protobuf bytes, for this JSON parser."],"exampleFix":"// before (wrong signal export)\n{ \"resourceMetrics\": [ ... ] }\n// after\n{ \"resourceSpans\": [ { \"scopeSpans\": [ { \"spans\": [ { \"traceId\": \"...\", \"spanId\": \"...\", \"name\": \"op\", \"startTimeUnixNano\": \"1\", \"endTimeUnixNano\": \"2\" } ] } ] } ] }","handlingStrategy":"validation","validationCode":"const doc = JSON.parse(text);\nconst spanCount = (doc.resourceSpans ?? []).flatMap((rs) => rs.scopeSpans ?? []).flatMap((ss) => ss.spans ?? []).length;\nif (spanCount === 0) throw new Error('Not an OTLP traces JSON export (no spans found)');","typeGuard":"const isOtlpTraceDoc = (d: unknown): d is { resourceSpans: { scopeSpans: { spans: unknown[] } }[] } =>\n  typeof d === 'object' && d !== null && 'resourceSpans' in d &&\n  Array.isArray((d as any).resourceSpans);","tryCatchPattern":"try {\n  traces = importTrace(text, file);\n} catch (e) {\n  if (e instanceof Error && e.message.includes('No spans found')) {\n    console.error('File has no spans: check signal type (traces vs metrics/logs) and OTLP JSON format');\n  } else throw e;\n}","preventionTips":["Confirm the exporter emits the traces signal (spans), not metrics or logs.","Use OTLP/HTTP with JSON encoding when feeding a JSON parser.","Check file size > 0 and that the export completed before importing."],"tags":["otlp","empty-result","ingest"],"backgroundTag":"empty-result-set","analyzedSha":"433685b2024e7bc4c99e1e2e326bcad39b4d9d65","analyzedAt":"2026-09-15T12:24:24.634Z","contentChangedAt":"2026-09-15T12:24:24.634Z","schemaVersion":2},"datasetVersion":"2026-09-22T16:17:23.217Z"}