{"record":{"id":"b69f71c8f0979cc0","repo":"JuliusBrussee/caveman","slug":"ccr-typed-object-put-w","errorCode":null,"errorMessage":"ccr typed object put: %w","messagePattern":"ccr typed object put: %w","errorType":"exception","errorClass":"ErrBudgetExceeded","httpStatus":null,"severity":"warning","filePath":"engine/ccr/store_sqlite.go","lineNumber":513,"sourceCode":"\t\treturn \"\", err\n\t}\n\tdeps, err := json.Marshal(obj.Dependencies)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"ccr typed object dependencies: %w\", err)\n\t}\n\tused, err := s.storageBytes()\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"ccr typed object budget: %w\", err)\n\t}\n\tvar existingBytes int64\n\terr = s.db.QueryRow(`SELECT length(data)+length(dependencies_json) FROM typed_objects WHERE object_id=?`, obj.ID).Scan(&existingBytes)\n\tif errors.Is(err, sql.ErrNoRows) {\n\t\texistingBytes = 0\n\t} else if err != nil {\n\t\treturn \"\", fmt.Errorf(\"ccr typed object budget: %w\", err)\n\t}\n\tif used-existingBytes+int64(len(obj.Data)+len(deps)) > s.maxBytes {\n\t\treturn \"\", fmt.Errorf(\"ccr typed object put: %w\", ErrBudgetExceeded)\n\t}\n\tresult, err := s.db.Exec(\n\t\t`INSERT INTO typed_objects (\n\t\t object_id, object_type, content_hash, source, created_at, repository_state,\n\t\t session_id, transform_version, currentness, lifecycle, dependencies_json,\n\t\t original_byte_length, stored_byte_length, data\n\t\t) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?)\n\t\tON CONFLICT(object_id) DO NOTHING`,\n\t\tobj.ID, obj.Type, obj.ContentHash, obj.Source, obj.CreatedAt.Format(time.RFC3339Nano),\n\t\tobj.RepositoryState, obj.SessionID, obj.TransformVersion, obj.Currentness,\n\t\tobj.Lifecycle, string(deps), obj.OriginalByteLength, obj.StoredByteLength, obj.Data,\n\t)\n\tif err != nil {\n\t\tif isFull(err) {\n\t\t\treturn \"\", fmt.Errorf(\"ccr typed object put: %w\", ErrBudgetExceeded)\n\t\t}\n\t\treturn \"\", fmt.Errorf(\"ccr typed object put: %w\", err)\n\t}","sourceCodeStart":495,"sourceCodeEnd":531,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/27d5a3981a347890211bb1bf2439e5c821a63bc9/engine/ccr/store_sqlite.go#L495-L531","documentation":"Returned when putting a typed object into the CCR store would push total storage past the configured maxBytes cap. It wraps the sentinel ErrBudgetExceeded, so callers can detect it with errors.Is. The check subtracts the bytes of any existing row with the same object_id (a re-put replaces, not adds) before comparing against the budget.","triggerScenarios":"PutTypedObject with obj.Data + dependencies_json large enough that used - existingBytes + newBytes > s.maxBytes; typically after the cache has accumulated entries near the cap, or a single very large object exceeds the whole budget.","commonSituations":"Long-lived cache that never evicts hitting its configured ceiling; maxBytes set too small for the workload's artifact sizes; a burst of large typed objects (e.g. big embedded blobs) filling the store.","solutions":["Treat this as an operational, non-fatal condition: the engine falls back to pass-through, so usually no action is needed beyond alerting.","Increase the store's maxBytes configuration if compression hit-rate matters for your workload.","Add eviction/GC of old typed_objects so used bytes stay under the cap.","If a single object exceeds maxBytes, chunk or exclude that artifact type from the store."],"exampleFix":"// before: treating any put error as fatal\nif _, err := store.PutTypedObject(ctx, obj); err != nil { return err }\n\n// after: recognize the sentinel and continue without CCR\nif _, err := store.PutTypedObject(ctx, obj); err != nil {\n    if errors.Is(err, ccr.ErrBudgetExceeded) { /* skip caching, keep going */ } else { return err }\n}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":"func isBudgetExceeded(err error) bool { return errors.Is(err, ccr.ErrBudgetExceeded) }","tryCatchPattern":"if _, err := store.PutTypedObject(obj); err != nil {\n    if errors.Is(err, ccr.ErrBudgetExceeded) {\n        // expected operational condition: skip caching, keep the pipeline running\n        return nil\n    }\n    return err\n}","preventionTips":["Size maxBytes to at least the sum of your largest expected artifacts plus headroom.","Monitor used-bytes metrics from the store so you see the ceiling approaching.","Evict stale typed_objects periodically instead of letting the store only grow."],"tags":["ccr","budget","storage","sentinel-error"],"backgroundTag":null,"analyzedSha":"27d5a3981a347890211bb1bf2439e5c821a63bc9","analyzedAt":"2026-08-15T09:26:11.751Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}