JuliusBrussee/caveman · error
ccr: typed object byte lengths cannot be negative
Error message
ccr: typed object byte lengths cannot be negative
What it means
prepareObject defaults zero byte-length fields to len(obj.Data) but rejects explicitly negative OriginalByteLength or StoredByteLength. Negative lengths would corrupt budget accounting and storage stats, so they fail closed. Only values < 0 are rejected; zero means 'fill in for me'.
Source
Thrown at engine/ccr/store.go:139
obj.CreatedAt = time.Now().UTC()
} else {
obj.CreatedAt = obj.CreatedAt.UTC()
}
dataSum := sha256.Sum256(obj.Data)
computedHash := "sha256:" + hex.EncodeToString(dataSum[:])
if obj.ContentHash == "" {
obj.ContentHash = computedHash
} else if obj.ContentHash != computedHash {
return Object{}, errors.New("ccr: typed object content_hash does not match data")
}
if obj.OriginalByteLength == 0 {
obj.OriginalByteLength = len(obj.Data)
}
if obj.StoredByteLength == 0 {
obj.StoredByteLength = len(obj.Data)
}
if obj.OriginalByteLength < 0 || obj.StoredByteLength < 0 {
return Object{}, errors.New("ccr: typed object byte lengths cannot be negative")
}
if obj.ID == "" {
identity := strings.Join([]string{string(obj.Type), obj.SessionID, obj.Source, obj.RepositoryState, obj.ContentHash}, "\x00")
sum := sha256.Sum256([]byte(identity))
obj.ID = "ccr_obj_" + hex.EncodeToString(sum[:16])
}
obj.Dependencies = append([]string(nil), obj.Dependencies...)
obj.Data = bytes.Clone(obj.Data)
return obj, nil
}
// sameImmutableObject compares fields PutObject promises never to change.
// CreatedAt is assigned at first storage, while currentness and lifecycle have
// explicit mutation methods, so repeat puts intentionally do not compare them.
func sameImmutableObject(left, right Object) bool {
return left.ID == right.ID &&
left.Type == right.Type &&
left.ContentHash == right.ContentHash &&View on GitHub (pinned to 27d5a3981a)
Solutions
- Leave the length fields at 0 so the store derives them from Data
- If you track pre-transform size, ensure OriginalByteLength is a non-negative count of the original bytes
- Clamp/validate lengths at your boundary before building the Object
Example fix
// before
obj := ccr.Object{OriginalByteLength: -1, Data: data} // -1 as 'unknown'
// after
obj := ccr.Object{Data: data} // lengths default to len(Data)
// or, when tracking the pre-compression size:
obj := ccr.Object{Data: packed, OriginalByteLength: len(original), StoredByteLength: len(packed)} Defensive patterns
Strategy: validation
Validate before calling
if obj.OriginalByteLength < 0 || obj.StoredByteLength < 0 {
return errors.New("byte lengths must be >= 0")
}
store.PutObject(obj) Type guard
func validByteLengths(o ccr.Object) bool {
return o.OriginalByteLength >= 0 && o.StoredByteLength >= 0
} Prevention
- Use 0 for 'unset', never -1 sentinels
- Guard subtraction with max(0, a-b) when deriving lengths
- Validate external records' numeric fields before building objects
When it happens
Trigger: Setting OriginalByteLength or StoredByteLength to -1 or any negative value (often as a sentinel for 'unknown'); arithmetic that underflows (e.g. len(data) - overhead where overhead > len(data)); deserializing a record with corrupt length fields.
Common situations: Using -1 as an 'unset' sentinel in a producer system; integer underflow when computing stored length after subtracting an estimate; hand-edited or externally produced JSON records with negative numbers.
Related errors
- ccr: typed object session_id is required
- cachebench: nil corpus reader
- message exceeds byte limit
- tool message requires tool_call_id
- cachebench: invalid corpus
AI-assisted analysis of JuliusBrussee/caveman@27d5a3981a (2026-08-15).
Data as JSON: /api/errors/673f1be4f0c2eccb.
Report an issue: GitHub.