{"record":{"id":"065c29b9781dc600","repo":"JuliusBrussee/caveman","slug":"ccr-storage-budget-exceeded","errorCode":null,"errorMessage":"ccr: storage budget exceeded","messagePattern":"ccr: storage budget exceeded","errorType":"exception","errorClass":"ErrBudgetExceeded","httpStatus":null,"severity":"warning","filePath":"engine/ccr/store.go","lineNumber":34,"sourceCode":"import (\n\t\"bytes\"\n\t\"crypto/sha256\"\n\t\"encoding/hex\"\n\t\"errors\"\n\t\"fmt\"\n\t\"slices\"\n\t\"strings\"\n\t\"time\"\n)\n\n// ErrNotFound is returned by Get when a handle is unknown. The store never\n// guesses a recovery — an unknown handle is an explicit miss.\nvar ErrNotFound = errors.New(\"ccr: recovery handle not found\")\n\n// ErrBudgetExceeded means a new recovery was refused before publishing lossy\n// bytes because the local store's configured payload budget would be exceeded.\n// Existing handles remain intact and retrievable; callers must pass through.\nvar ErrBudgetExceeded = errors.New(\"ccr: storage budget exceeded\")\n\n// ObjectType is a closed typed-working-memory enum. Unknown values fail closed:\n// adapters may preserve unknown native payloads outside CCR, but may not invent\n// retrieval semantics for them.\ntype ObjectType string\n\nconst (\n\tObjectFileObservation      ObjectType = \"FileObservation\"\n\tObjectSearchResult         ObjectType = \"SearchResult\"\n\tObjectCommandResult        ObjectType = \"CommandResult\"\n\tObjectTestResult           ObjectType = \"TestResult\"\n\tObjectBuildResult          ObjectType = \"BuildResult\"\n\tObjectDiffSnapshot         ObjectType = \"DiffSnapshot\"\n\tObjectTaskContract         ObjectType = \"TaskContract\"\n\tObjectTaskDecision         ObjectType = \"TaskDecision\"\n\tObjectExecutionState       ObjectType = \"ExecutionState\"\n\tObjectDocumentationExcerpt ObjectType = \"DocumentationExcerpt\"\n\tObjectBrowserSnapshot      ObjectType = \"BrowserSnapshot\"","sourceCodeStart":16,"sourceCodeEnd":52,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/27d5a3981a347890211bb1bf2439e5c821a63bc9/engine/ccr/store.go#L16-L52","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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)"],"exampleFix":"// before\n_, err := store.Put(recovery)\nif err != nil {\n    return err // aborts the whole request\n}\n\n// after\n_, err := store.Put(recovery)\nif errors.Is(err, ccr.ErrBudgetExceeded) {\n    // contract: callers must pass through; existing handles unaffected\n    return nil\n}\nif err != nil {\n    return err\n}","handlingStrategy":"fallback","validationCode":null,"typeGuard":null,"tryCatchPattern":"_, err := store.Put(recovery)\nswitch {\ncase errors.Is(err, ccr.ErrBudgetExceeded):\n    // documented contract: pass through, existing handles intact\ncase err != nil:\n    return err\n}","preventionTips":["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"],"tags":["ccr","go","backpressure","storage-budget","sentinel-error"],"backgroundTag":null,"analyzedSha":"27d5a3981a347890211bb1bf2439e5c821a63bc9","analyzedAt":"2026-08-15T09:26:11.751Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}