JuliusBrussee/caveman · error

encode before evidence: %w

Error message

encode before evidence: %w

What it means

json.Marshal of the before-evidence map failed in RecordAppliedFix, before any database write. The map copies the sink's evidence and adds before_source; marshaling fails only if evidence contains values JSON cannot encode (channels, funcs, cyclic data).

Source

Thrown at proxy/internal/store/learn_outcomes.go:93

	}
	if fixKind == "" {
		return AppliedFixRecord{}, fmt.Errorf("sink %q has no measurable fix kind; pass --fix-kind explicitly", sinkID)
	}
	if len(fixKind) > 128 {
		return AppliedFixRecord{}, fmt.Errorf("fix kind exceeds 128 bytes")
	}
	if len(note) > 4096 {
		return AppliedFixRecord{}, fmt.Errorf("note exceeds 4096 bytes")
	}
	appliedAt := appliedAtTime.Format(time.RFC3339)
	beforeEvidence := make(map[string]any, len(sink.Evidence)+1)
	for key, value := range sink.Evidence {
		beforeEvidence[key] = value
	}
	beforeEvidence["before_source"] = beforeSource
	evidence, err := json.Marshal(beforeEvidence)
	if err != nil {
		return AppliedFixRecord{}, fmt.Errorf("encode before evidence: %w", err)
	}
	// Fingerprint the edited artifact as it stands NOW — which at
	// `caveman learn applied` time is immediately after the approved edit. This
	// is the provenance anchor: a later scan re-hashes the same path and can
	// state whether the change we proposed is still the change that is there.
	target := fingerprintFixTarget(*sink)
	targetJSON := ""
	if target != nil {
		if encoded, encodeErr := json.Marshal(target); encodeErr == nil {
			targetJSON = string(encoded)
		}
	}
	result, err := s.db.Exec(`INSERT INTO applied_fixes
		(sink_id, practice_id, fix_kind, applied_at, before_tokens_per_turn, before_evidence_json, note, target_json)
		VALUES (?, ?, ?, ?, ?, ?, ?, ?)`, sink.SinkID, sink.PracticeID, fixKind, appliedAt, sink.TokensPerTurn, string(evidence), note, targetJSON)
	if err != nil {
		return AppliedFixRecord{}, fmt.Errorf("record applied fix: %w", err)
	}

View on GitHub (pinned to 81536f57b3)

Solutions

  1. Inspect the sink evidence map for non-JSON-serializable values
  2. Sanitize or drop unsupported evidence values before recording the fix
  3. Log the wrapped json error to identify the offending key
Defensive patterns

Strategy: fallback

When it happens

Trigger: Thrown at proxy/internal/store/learn_outcomes.go:93 when the library encounters an invalid state.

Common situations: See trigger scenarios.


AI-assisted analysis of JuliusBrussee/caveman@81536f57b3 (2026-08-27). Data as JSON: /api/errors/5e92cdd4bdc08e18. Report an issue: GitHub.