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 rejects typed Objects whose OriginalByteLength or StoredByteLength are negative. These are only defaulted (to len(Data)) when zero; negative values are treated as invalid metadata and the put fails closed rather than storing inconsistent size accounting.
Solutions
- Leave both length fields zero so prepareObject defaults them to len(obj.Data).
- Clamp computed lengths to >= 0 before calling PutObject.
- Validate any unmarshaled byteLength values are non-negative at deserialization time.
- Fix the size computation that produced the negative value (usually a subtraction underflow).
Example fix
// before
obj.StoredByteLength = len(raw) - len(comp) // may go negative
// after
if n := len(raw) - len(comp); n >= 0 {
obj.StoredByteLength = n
} // else leave 0 to let prepareObject default it Defensive patterns
Strategy: validation
Validate before calling
if obj.OriginalByteLength < 0 || obj.StoredByteLength < 0 {
return errors.New("negative byte lengths")
} Type guard
func saneLengths(o ccr.Object) bool {
return o.OriginalByteLength >= 0 && o.StoredByteLength >= 0
} Try / catch
if err := clampLengths(&obj); err != nil { return err }
if _, err := store.PutObject(obj); err != nil {
if strings.Contains(err.Error(), "cannot be negative") {
return fmt.Errorf("size accounting bug: %w", err)
}
return err
} Prevention
- Leave length fields zero to use the len(Data) default.
- Guard any subtraction-based size math against underflow.
- Validate unmarshaled byteLength values are non-negative.
- Add a fuzz/property test over size computations.
When it happens
Trigger: PutObject with an Object built from int arithmetic that can go negative (e.g. length computed from a subtraction, or a negative value unmarshaled from JSON), while len(Data) > 0 so the zero-defaulting never applies.
Common situations: Size fields populated by buggy calculations (stored = original - compressed); JSON payloads with negative byteLength accepted upstream; integer underflow when computing compressed savings.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- ccr: typed object session_id is required
- CAVEMAN_CCR_MAX_BYTES must be a positive integer
- ccr: typed object content_hash does not match data
- apiKey, baseURL, and agent are required
- arm must be or
AI-assisted analysis of JuliusBrussee/caveman@3ee70a1026 (2026-09-20).
Data as JSON: /api/errors/673f1be4f0c2eccb.
Report an issue: GitHub.
Appendix: source
Thrown at engine/ccr/store.go:143
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 3ee70a1026)