JuliusBrussee/caveman · warning · ErrBudgetExceeded
ccr: storage budget exceeded
Error message
ccr: storage budget exceeded
What it means
A new recovery was refused before any lossy bytes were published because the local store's configured payload budget would be exceeded. This is a deliberate backpressure mechanism: existing handles stay intact and retrievable, and the contract requires callers to pass through (continue without the new recovery) rather than fail the whole operation.
Source
Thrown at engine/ccr/store.go:34
import (
"bytes"
"crypto/sha256"
"encoding/hex"
"errors"
"fmt"
"slices"
"strings"
"time"
)
// ErrNotFound is returned by Get when a handle is unknown. The store never
// guesses a recovery — an unknown handle is an explicit miss.
var ErrNotFound = errors.New("ccr: recovery handle not found")
// ErrBudgetExceeded means a new recovery was refused before publishing lossy
// bytes because the local store's configured payload budget would be exceeded.
// Existing handles remain intact and retrievable; callers must pass through.
var ErrBudgetExceeded = errors.New("ccr: storage budget exceeded")
// ObjectType is a closed typed-working-memory enum. Unknown values fail closed:
// adapters may preserve unknown native payloads outside CCR, but may not invent
// retrieval semantics for them.
type ObjectType string
const (
ObjectFileObservation ObjectType = "FileObservation"
ObjectSearchResult ObjectType = "SearchResult"
ObjectCommandResult ObjectType = "CommandResult"
ObjectTestResult ObjectType = "TestResult"
ObjectBuildResult ObjectType = "BuildResult"
ObjectDiffSnapshot ObjectType = "DiffSnapshot"
ObjectTaskContract ObjectType = "TaskContract"
ObjectTaskDecision ObjectType = "TaskDecision"
ObjectExecutionState ObjectType = "ExecutionState"
ObjectDocumentationExcerpt ObjectType = "DocumentationExcerpt"
ObjectBrowserSnapshot ObjectType = "BrowserSnapshot"View on GitHub (pinned to 27d5a3981a)
Solutions
- Handle ErrBudgetExceeded by passing through: skip storing this recovery and continue, per the documented contract
- Raise the store's payload budget configuration if the workload legitimately needs more
- Prune/archive old recoveries (lifecycle to archived/cold) before retrying if retention policy allows
- Reduce the size of the payload being stored (compress or narrow the recovery data)
Example fix
// before
_, err := store.Put(recovery)
if err != nil {
return err // aborts the whole request
}
// after
_, err := store.Put(recovery)
if errors.Is(err, ccr.ErrBudgetExceeded) {
// contract: callers must pass through; existing handles unaffected
return nil
}
if err != nil {
return err
} Defensive patterns
Strategy: fallback
Try / catch
_, err := store.Put(recovery)
switch {
case errors.Is(err, ccr.ErrBudgetExceeded):
// documented contract: pass through, existing handles intact
case err != nil:
return err
} Prevention
- Size the payload budget to expected session length, not the minimum
- Track stored bytes vs budget in metrics so exhaustion is visible before it happens
- Never retry the same Put on budget errors without pruning first
When it happens
Trigger: Calling Put/Publish on the CCR store when total stored payload bytes plus the new recovery would exceed the configured budget; repeatedly publishing large recoveries without pruning; small budget configured for tests hitting real workloads.
Common situations: Long-running sessions accumulating recoveries until the budget is exhausted; misconfigured budget value (unset, zero, or sized for a smaller workload); one unusually large recovery payload (e.g. a big diff snapshot) exhausting the remaining headroom.
Related errors
- ccr: recovery handle not found
- ccr: typed object session_id is required
- ccr: typed object content_hash does not match data
- ccr: typed object byte lengths cannot be negative
- ccr: typed object id collision
AI-assisted analysis of JuliusBrussee/caveman@27d5a3981a (2026-08-15).
Data as JSON: /api/errors/065c29b9781dc600.
Report an issue: GitHub.