JuliusBrussee/caveman · error
proposalrun: seq recompute
Error message
proposalrun: seq %d recompute: %w
What it means
VerifyChain recomputes each row's hash via RowHash; this error wraps a failure from that recomputation (returned with %w, typically from CanonicalDetail failing to canonicalize the stored detail JSON). It means the stored row data itself is unusable for hashing — most often the detail column holds invalid or non-canonicalizable JSON.
Solutions
- Inspect the wrapped cause (errors.Unwrap / %w chain) and fix or restore the offending row's detail JSON.
- Re-validate detail with json.Valid before rows are written; write only via the library's Append helper.
- Restore the affected rows from backup or regenerate them from the original events.
- If the wrapped error is an oversized JSON number, re-serialize the detail with bounded numeric precision.
Example fix
// before
Append(action, json.RawMessage(badBytes), cost) // badBytes is not valid JSON
// after
if !json.Valid(badBytes) { return fmt.Errorf("invalid detail") }
Append(action, json.RawMessage(badBytes), cost) Defensive patterns
Strategy: try-catch
Validate before calling
func detailIsValid(runs []proposalrun.Run) bool {
for _, r := range runs {
if !json.Valid(r.Detail) { return false }
}
return true
} Try / catch
if err := proposalrun.VerifyChain(runs); err != nil {
var inner error
errors.As(err, &inner) // inspect wrapped recompute cause
return fmt.Errorf("verify failed: %w", err)
} Prevention
- Check json.Valid(detail) before every append
- Restrict detail writes to the library's canonicalization path
- Monitor for canonicalization errors in write telemetry
When it happens
Trigger: Calling VerifyChain(runs) where some row's Detail is malformed JSON or otherwise fails CanonicalDetail, causing RowHash to return an error which is wrapped as 'seq %d recompute: %w'.
Common situations: Detail written by non-library code paths with invalid JSON, encoding corrupted by migrations or column type changes, truncated JSON from an oversized payload, invalid JSON numbers as in errors 1510-1515.
Understand the failure class
Background: JSON parse error: "Unexpected token" / "not valid JSON" / "failed to parse" — what JSON parsers are really complaining about — this error's family across 45 libraries.
Related errors
- proposalrun: seq out of order (expected )
- proposalrun: seq prev_hash does not link to the prior row
- proposalrun: canonicalize detail
- proposalrun: marshal canonical detail
- proposalrun: seq row_hash mismatch (tampered)
AI-assisted analysis of JuliusBrussee/caveman@3ee70a1026 (2026-09-20).
Data as JSON: /api/errors/bb782f2b337b3330.
Report an issue: GitHub.
Appendix: source
Thrown at shared/platform/proposalrun/proposalrun.go:361
if len(runs) == 0 {
return nil
}
sorted := make([]Run, len(runs))
copy(sorted, runs)
sort.Slice(sorted, func(i, j int) bool { return sorted[i].Seq < sorted[j].Seq })
prevHash := ""
for i, r := range sorted {
wantSeq := int64(i + 1)
if r.Seq != wantSeq {
return fmt.Errorf("proposalrun: seq %d out of order (expected %d)", r.Seq, wantSeq)
}
if r.PrevHash != prevHash {
return fmt.Errorf("proposalrun: seq %d prev_hash does not link to the prior row", r.Seq)
}
got, err := RowHash(r.PrevHash, r.Seq, r.Action, r.Detail, r.CostUSD, r.CreatedAt)
if err != nil {
return fmt.Errorf("proposalrun: seq %d recompute: %w", r.Seq, err)
}
if got != r.RowHash {
return fmt.Errorf("proposalrun: seq %d row_hash mismatch (tampered)", r.Seq)
}
prevHash = r.RowHash
}
return nil
}
View on GitHub (pinned to 3ee70a1026)