JuliusBrussee/caveman · error
note exceeds 4096 bytes
Error message
note exceeds 4096 bytes
What it means
The optional free-form note attached to an applied-fix record is persisted in SQLite and echoed in reports. RecordAppliedFix caps it at 4096 bytes to bound row size. Any note longer than that is rejected outright.
Solutions
- Truncate the note to 4096 bytes before calling RecordAppliedFix
- Move long explanations into a separate file and reference it in a short note
- Validate note length in the CLI layer with a friendly message
Example fix
// before
rec, err := store.RecordAppliedFix(ctx, sink, fixKind, longNote)
// after
if len(longNote) > 4096 {
longNote = longNote[:4096]
}
rec, err := store.RecordAppliedFix(ctx, sink, fixKind, longNote) Defensive patterns
Strategy: validation
Validate before calling
if len(note) > 4096 { note = note[:4096] } // or reject with a clear message Try / catch
if _, err := store.RecordAppliedFix(ctx, sink, kind, note); err != nil { if strings.Contains(err.Error(), "note exceeds 4096") { note = note[:4096]; retry } } Prevention
- Truncate notes defensively before calling the store
- Keep long analysis in files and reference them from short notes
- Enforce a smaller internal note limit (e.g. 2000 chars) in your tooling
When it happens
Trigger: Calling RecordAppliedFix with a note argument longer than 4096 bytes.
Common situations: Pasting long analysis text or a full diff into --note; scripting tools that write logs as the note.
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
- fix kind exceeds 128 bytes
- experiment label must be 1-128 bytes
- note exceeds 4096 bytes
- sink has no measurable fix kind; pass --fix-kind explicitly
- usage export has no recognizable model column
AI-assisted analysis of JuliusBrussee/caveman@3ee70a1026 (2026-09-20).
Data as JSON: /api/errors/ebd1ad9f63ff652f.
Report an issue: GitHub.
Appendix: source
Thrown at proxy/internal/store/learn_outcomes.go:83
// 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.
target := fingerprintFixTarget(*sink)
targetJSON := ""
if target != nil {View on GitHub (pinned to 3ee70a1026)