JuliusBrussee/caveman · error

mapped span_id is empty

Error message

mapped span_id is empty

What it means

Same final validation as 1183 but for SpanID: the mapped span_id path resolved, yet the resulting string is empty after trimming. Every span needs a non-empty span ID because it is the primary identifier in caveman.spans. The check runs after trace_id, so trace ID was valid on this record.

Source

Thrown at shared/platform/importers/generic.go:162

			assignInt(&sp, target, parseFlexInt(val))
		case "float":
			sp.TotalCostUSD = roundUSD(parseFlexFloat(val))
		case "time":
			s, ns := parseTimeFlexible(val)
			if target == "timestamp" {
				sp.Timestamp = s
				startNs = ns
			} else {
				sp.EndTimestamp = s
				endNs = ns
			}
		}
	}
	if strings.TrimSpace(sp.TraceID) == "" {
		return Span{}, nil, fmt.Errorf("mapped trace_id is empty")
	}
	if strings.TrimSpace(sp.SpanID) == "" {
		return Span{}, nil, fmt.Errorf("mapped span_id is empty")
	}
	if sp.Timestamp == "" {
		return Span{}, nil, fmt.Errorf("mapped timestamp is missing or invalid")
	}
	if d := durationMS(startNs, endNs); d > 0 {
		sp.DurationMS = d
	}
	sp.Attributes = map[string]string{"import.source": "generic"}
	applyScope(&sp, opts)
	return sp, resolved, nil
}

func assignString(sp *Span, target, v string) {
	switch target {
	case "trace_id":
		sp.TraceID = v
	case "span_id":
		sp.SpanID = v

View on GitHub (pinned to 27d5a3981a)

Solutions

  1. Inspect the failing record and populate or drop it upstream
  2. Re-point the span_id mapping at the field that actually holds a non-empty value on every record
  3. Synthesize span IDs during pre-processing if the source genuinely lacks them
  4. Verify the mapping is not pointing at trace_id's field on records where both coincide

Example fix

// before
rec := map[string]any{"traceId": "t1", "spanId": nil, "started_at": "2026-01-01T00:00:00Z"}

// after
rec := map[string]any{"traceId": "t1", "spanId": "s1", "started_at": "2026-01-01T00:00:00Z"}
Defensive patterns

Strategy: validation

Validate before calling

func hasNonEmptySpanID(rec map[string]any, path string) bool {
	v, ok := lookupPath(rec, path)
	if !ok { return false }
	s, ok := v.(string)
	return ok && strings.TrimSpace(s) != ""
}

Type guard

func isUsableID(v any) bool {
	s, ok := v.(string)
	return ok && strings.TrimSpace(s) != ""
}

Try / catch

if strings.Contains(err.Error(), "span_id is empty") { /* drop or generate span ID for that record */ }

Prevention

When it happens

Trigger: Record has span_id present but empty/null/whitespace; span ID stored under a different populated field while the mapped one is a placeholder; asString of a non-string value (object, array) yielding an empty result.

Common situations: Exports where parent-only or synthetic rows carry an empty span ID; partial renames in the source schema leaving the old key present but blank; mapping span_id to a field that only exists on child spans.

Related errors


AI-assisted analysis of JuliusBrussee/caveman@27d5a3981a (2026-08-15). Data as JSON: /api/errors/4315e8062f2240b8. Report an issue: GitHub.