JuliusBrussee/caveman · error

langfuse decode: %w

Error message

langfuse decode: %w

What it means

decodeLangfuse tries a langfuseEnvelope with an Observations array first, then a bare []langfuseObservation; if the bare-array unmarshal also fails, its json error is wrapped as 'langfuse decode: %w'. The wrapped message distinguishes shape problems ('invalid character ...') from field-type mismatches on a specific observation property.

Source

Thrown at shared/platform/importers/langfuse.go:63

	obs, err := decodeLangfuse(data)
	if err != nil {
		return nil, err
	}
	rows := make([]Span, 0, len(obs))
	for _, o := range obs {
		rows = append(rows, mapLangfuse(o, opts))
	}
	return rows, nil
}

func decodeLangfuse(data []byte) ([]langfuseObservation, error) {
	var env langfuseEnvelope
	if err := json.Unmarshal(data, &env); err == nil && env.Observations != nil {
		return env.Observations, nil
	}
	var arr []langfuseObservation
	if err := json.Unmarshal(data, &arr); err != nil {
		return nil, fmt.Errorf("langfuse decode: %w", err)
	}
	return arr, nil
}

func mapLangfuse(o langfuseObservation, opts Options) Span {
	start, startNs := parseTimeFlexible(o.StartTime)
	end, endNs := parseTimeFlexible(o.EndTime)

	status := "ok"
	switch o.Level {
	case "ERROR":
		status = "error"
	case "WARNING":
		status = "unset"
	}

	spanType := o.Type
	if spanType == "" {

View on GitHub (pinned to 27d5a3981a)

Solutions

  1. Check the wrapped json error to see if the top-level shape or a typed field failed
  2. Ensure input is an observations export: {"observations":[...]} or a bare array of observation objects
  3. If the field type changed upstream, coerce it to the expected type in pre-processing
  4. Confirm the saved file is complete JSON, not a truncated or multi-document stream

Example fix

// before
raw := []byte(`{"traces":[{"id":"t1"}]}`) // wrong export
rows, sum, err := importers.Import(importers.FormatLangfuse, raw, opts)

// after
raw := []byte(`{"observations":[{"id":"o1","startTime":"2026-01-01T00:00:00Z"}]}`)
rows, sum, err := importers.Import(importers.FormatLangfuse, raw, opts)
Defensive patterns

Strategy: validation

Validate before calling

func looksLikeLangfuse(data []byte) bool {
	d := bytes.TrimSpace(data)
	return len(d) > 0 && (d[0] == '[' || bytes.Contains(d, []byte("\"observations\"")))
}

Try / catch

if _, _, err := importers.Import(importers.FormatLangfuse, raw, opts); err != nil { if strings.Contains(err.Error(), "langfuse decode") { return fmt.Errorf("not a Langfuse observations export: %w", err) } }

Prevention

When it happens

Trigger: Passing the Langfuse traces export instead of the observations export (different schema); an object wrapper keyed differently than "observations"; an observation whose typed field (e.g. latencyNumbers) holds the wrong JSON type; empty or truncated input.

Common situations: Downloading the wrong export artifact from Langfuse UI (traces vs observations); API response shape change between Langfuse versions; saving an error body from a rate-limited request; concatenating NDJSON pages.

Related errors


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