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
- Pass an explicit fix kind via the --fix-kind flag (e.g. the CLI's supported values).
- Check len/trim: the value must be non-empty after trimming and at most 128 bytes.
- Add support for the new sink type in defaultFixKind so a measurable default is derived.
- 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
- Always pass --fix-kind in scripted/CI usage
- Extend defaultFixKind for new sink types
- Keep fixKind <= 128 bytes and trimmed
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
- arm must be or
- ccr max storage bytes must be positive
- rewriter: theta must not be negative, got
- artifactId is required
- cannot supersede with empty text
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 atView on GitHub (pinned to 3ee70a1026)