JuliusBrussee/caveman · error

fix kind exceeds 128 bytes

Error message

fix kind exceeds 128 bytes

What it means

RecordAppliedFix records an applied fix outcome for a sink, and the fixKind string is used as a DB column and report key. The store rejects any fix kind longer than 128 bytes so records stay bounded and comparable. The value may come from the caller or from defaultFixKind; the length check runs after defaulting.

Solutions

  1. Shorten the fixKind string to 128 bytes or fewer before calling RecordAppliedFix
  2. Check len(fixKind) in the caller and surface a clear CLI validation message before reaching the store
  3. Ensure defaultFixKind on the sink returns a short, stable kind identifier

Example fix

// before
rec, err := store.RecordAppliedFix(ctx, sink, userSuppliedKind, note)
// after
if len(userSuppliedKind) > 128 {
    return fmt.Errorf("--fix-kind must be at most 128 bytes")
}
rec, err := store.RecordAppliedFix(ctx, sink, userSuppliedKind, note)
Defensive patterns

Strategy: validation

Validate before calling

if len(fixKind) > 128 { return fmt.Errorf("fix kind must be <= 128 bytes, got %d", len(fixKind)) }

Try / catch

if rec, err := store.RecordAppliedFix(ctx, sink, fixKind, note); err != nil { if strings.Contains(err.Error(), "exceeds 128 bytes") { /* prompt user for shorter --fix-kind */ } }

Prevention

When it happens

Trigger: Calling RecordAppliedFix with a fixKind argument (or a sink whose defaultFixKind derives a value) whose UTF-8 length exceeds 128 bytes.

Common situations: Passing a whole description or CLI note string as --fix-kind; programmatic callers building fixKind from concatenated labels; upstream schema changes making defaultFixKind produce longer strings.

Understand the failure class

Background: "value must be between 0 and 1" / "out of range" / "must not be negative" errors: fixing range-validation failures across open-source libraries — this error's family across 42 libraries.

Related errors


AI-assisted analysis of JuliusBrussee/caveman@3ee70a1026 (2026-09-20). Data as JSON: /api/errors/c5df80d42e37a422. Report an issue: GitHub.

Appendix: source

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

		}
	}
	appliedAtTime := learnOutcomeClock().UTC()
	// A scan run after the user's edit is post-fix evidence. Prefer newest report
	// snapshot strictly older than this plan's scan; use live plan only when no
	// older snapshot contains the sink. Missing live sinks may use snapshot only.
	sink, beforeSource := beforeSinkForAppliedFix(plan, sinkID, liveSink, appliedAtTime)
	if sink == nil {
		return AppliedFixRecord{}, fmt.Errorf("sink %q not found in current learn plan", sinkID)
	}
	fixKind = strings.TrimSpace(fixKind)
	if fixKind == "" {
		fixKind = defaultFixKind(*sink)
	}
	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.

View on GitHub (pinned to 3ee70a1026)