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

  1. Handle ErrBudgetExceeded by passing through: skip storing this recovery and continue, per the documented contract
  2. Raise the store's payload budget configuration if the workload legitimately needs more
  3. Prune/archive old recoveries (lifecycle to archived/cold) before retrying if retention policy allows
  4. 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

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


AI-assisted analysis of JuliusBrussee/caveman@27d5a3981a (2026-08-15). Data as JSON: /api/errors/065c29b9781dc600. Report an issue: GitHub.