JuliusBrussee/caveman · warning · ErrBudgetExceeded

ccr typed object put: %w

Error message

ccr typed object put: %w

What it means

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.

Source

Thrown at engine/ccr/store_sqlite.go:513

		return "", err
	}
	deps, err := json.Marshal(obj.Dependencies)
	if err != nil {
		return "", fmt.Errorf("ccr typed object dependencies: %w", err)
	}
	used, err := s.storageBytes()
	if err != nil {
		return "", fmt.Errorf("ccr typed object budget: %w", err)
	}
	var existingBytes int64
	err = s.db.QueryRow(`SELECT length(data)+length(dependencies_json) FROM typed_objects WHERE object_id=?`, obj.ID).Scan(&existingBytes)
	if errors.Is(err, sql.ErrNoRows) {
		existingBytes = 0
	} else if err != nil {
		return "", fmt.Errorf("ccr typed object budget: %w", err)
	}
	if used-existingBytes+int64(len(obj.Data)+len(deps)) > s.maxBytes {
		return "", fmt.Errorf("ccr typed object put: %w", ErrBudgetExceeded)
	}
	result, err := s.db.Exec(
		`INSERT INTO typed_objects (
		 object_id, object_type, content_hash, source, created_at, repository_state,
		 session_id, transform_version, currentness, lifecycle, dependencies_json,
		 original_byte_length, stored_byte_length, data
		) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?)
		ON CONFLICT(object_id) DO NOTHING`,
		obj.ID, obj.Type, obj.ContentHash, obj.Source, obj.CreatedAt.Format(time.RFC3339Nano),
		obj.RepositoryState, obj.SessionID, obj.TransformVersion, obj.Currentness,
		obj.Lifecycle, string(deps), obj.OriginalByteLength, obj.StoredByteLength, obj.Data,
	)
	if err != nil {
		if isFull(err) {
			return "", fmt.Errorf("ccr typed object put: %w", ErrBudgetExceeded)
		}
		return "", fmt.Errorf("ccr typed object put: %w", err)
	}

View on GitHub (pinned to 27d5a3981a)

Solutions

  1. Treat this as an operational, non-fatal condition: the engine falls back to pass-through, so usually no action is needed beyond alerting.
  2. Increase the store's maxBytes configuration if compression hit-rate matters for your workload.
  3. Add eviction/GC of old typed_objects so used bytes stay under the cap.
  4. If a single object exceeds maxBytes, chunk or exclude that artifact type from the store.

Example fix

// before: treating any put error as fatal
if _, err := store.PutTypedObject(ctx, obj); err != nil { return err }

// after: recognize the sentinel and continue without CCR
if _, err := store.PutTypedObject(ctx, obj); err != nil {
    if errors.Is(err, ccr.ErrBudgetExceeded) { /* skip caching, keep going */ } else { return err }
}
Defensive patterns

Strategy: try-catch

Type guard

func isBudgetExceeded(err error) bool { return errors.Is(err, ccr.ErrBudgetExceeded) }

Try / catch

if _, err := store.PutTypedObject(obj); err != nil {
    if errors.Is(err, ccr.ErrBudgetExceeded) {
        // expected operational condition: skip caching, keep the pipeline running
        return nil
    }
    return err
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


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