JuliusBrussee/caveman · error

sink has no measurable fix kind; pass --fix-kind explicitly

Error message

sink %q has no measurable fix kind; pass --fix-kind explicitly

What it means

RecordAppliedFix derives a default fix kind from the sink via defaultFixKind; if the caller passed none and the sink has no measurable default, recording is rejected because the outcome analysis needs a fix kind to bucket results.

Solutions

  1. Pass an explicit fix kind via the --fix-kind flag (e.g. the CLI's supported values).
  2. Check len/trim: the value must be non-empty after trimming and at most 128 bytes.
  3. Add support for the new sink type in defaultFixKind so a measurable default is derived.
  4. Record the fix against a sink that does have a measurable fix kind if the current one is irrelevant.

Example fix

// before
proxy learn record-fix --sink sql-slow --note "added index"
// after
proxy learn record-fix --sink sql-slow --fix-kind timeout --note "added index"
Defensive patterns

Strategy: validation

Validate before calling

fixKind = strings.TrimSpace(fixKind)
if fixKind == "" { fixKind = "timeout" } // or require --fix-kind
if fixKind == "" || len(fixKind) > 128 { return errors.New("invalid fix kind") }

Try / catch

_, err := store.RecordAppliedFix(plan, liveSink, sinkID, fixKind, note)
if err != nil && strings.Contains(err.Error(), "no measurable fix kind") {
    return fmt.Errorf("pass --fix-kind explicitly for sink %q", sinkID)
}

Prevention

When it happens

Trigger: Calling Store.RecordAppliedFix with an empty fixKind for a sink whose defaultFixKind returns "" — the sink lacks the properties (e.g. timeout or similar measurable knob) that imply a fix kind.

Common situations: User runs 'proxy learn record-fix <sink>' without --fix-kind on a sink with no inherent measurable fix; a new sink type added to the plan that defaultFixKind does not recognize yet.

Understand the failure class

Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.

Related errors


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

Appendix: source

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

		if plan.Sinks[i].SinkID == sinkID {
			liveSink = &plan.Sinks[i]
			break
		}
	}
	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

View on GitHub (pinned to 3ee70a1026)