JuliusBrussee/caveman · error

helicone decode: %w

Error message

helicone decode: %w

What it means

decodeHelicone tries two shapes: a heliconeEnvelope object with a Data array, then a bare array of heliconeRecords. When both json.Unmarshal attempts fail, the second (bare-array) error is wrapped as 'helicone decode: %w'. The wrapped error is Go's encoding/json detail (e.g. 'invalid character ... looking for beginning of value' or a type mismatch on a record field).

Source

Thrown at shared/platform/importers/helicone.go:67

	recs, err := decodeHelicone(data)
	if err != nil {
		return nil, err
	}
	rows := make([]Span, 0, len(recs))
	for _, rec := range recs {
		rows = append(rows, mapHelicone(rec, opts))
	}
	return rows, nil
}

func decodeHelicone(data []byte) ([]heliconeRecord, error) {
	var env heliconeEnvelope
	if err := json.Unmarshal(data, &env); err == nil && env.Data != nil {
		return env.Data, nil
	}
	var arr []heliconeRecord
	if err := json.Unmarshal(data, &arr); err != nil {
		return nil, fmt.Errorf("helicone decode: %w", err)
	}
	return arr, nil
}

func mapHelicone(rec heliconeRecord, opts Options) Span {
	ts, _ := parseTimeFlexible(rec.CreatedAt)

	model := rec.Response.Model
	if model == "" {
		model = rec.Request.Model
	}
	provider := rec.Provider
	if provider == "" {
		provider = rec.Request.Provider
	}

	// 2xx is ok; anything else (including 0/unknown) is an error.
	status := "ok"

View on GitHub (pinned to 27d5a3981a)

Solutions

  1. Read the wrapped json error to identify whether the problem is overall shape or a specific field type
  2. Ensure input is one JSON document: either {"data":[...]} or a bare array of records
  3. If a record field changed type upstream, transform it to the expected type before import
  4. Verify the saved export is not an error payload or empty response

Example fix

// before
raw := []byte(`{"error":"unauthorized"}`)
rows, sum, err := importers.Import(importers.FormatHelicone, raw, opts)

// after
raw := []byte(`{"data":[{"id":"r1","created_at":"2026-01-01T00:00:00Z"}]}`)
rows, sum, err := importers.Import(importers.FormatHelicone, raw, opts)
Defensive patterns

Strategy: validation

Validate before calling

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

Try / catch

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

Prevention

When it happens

Trigger: Passing a bare JSON object that is neither shape; passing a JSONL stream; a record where a fixed-type field (e.g. numeric) holds a string, causing unmarshal to fail on []heliconeRecord; empty input bytes; truncated response body saved from a paged API call.

Common situations: Saving only the last page of a paginated Helicone export incorrectly; schema change in Helicone API responses changing a field's JSON type; concatenating multiple pages without wrapping them in an array; an auth-error JSON body {"error":...} saved instead of data.

Related errors


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