JuliusBrussee/caveman · error
message exceeds byte limit
Error message
message exceeds byte limit
What it means
Thrown by corpus message validation when the accumulated byte size of a message (role + content + tool call fields) exceeds CorpusLimits.MaxMessageBytes. The library enforces this bound so a single pathological corpus row cannot dominate memory or hashing during corpus analysis. It fires per message, before the row is appended to the corpus.
Source
Thrown at cacheengine/cachebench/corpus.go:380
if len(message.Content) > 0 {
var content any
if err := json.Unmarshal(message.Content, &content); err != nil {
return errors.New("content must decode")
}
if content != nil {
if _, ok := content.(string); !ok {
return errors.New("content must be string or null")
}
}
}
for _, call := range message.ToolCalls {
if call.Type != "function" || !validBoundedText(call.ID, 2048, false) || !validBoundedText(call.Function.Name, 512, false) || !validUniqueJSONObject([]byte(call.Function.Arguments)) {
return errors.New("tool call requires function type, id, function name, and JSON arguments")
}
messageBytes += len(call.ID) + len(call.Type) + len(call.Function.Name) + len(call.Function.Arguments)
}
if messageBytes > limits.MaxMessageBytes {
return errors.New("message exceeds byte limit")
}
if message.Role == "tool" && message.ToolCallID == "" {
return errors.New("tool message requires tool_call_id")
}
return nil
}
func appendCorpusRow(rows *[]CorpusRow, sessions map[string]bool, retainedBytes *int64, row CorpusRow, limits CorpusLimits) error {
if len(*rows) >= limits.MaxRows {
return fmt.Errorf("row limit %d exceeded", limits.MaxRows)
}
if !sessions[row.SessionID] {
if len(sessions) >= limits.MaxSessions {
return fmt.Errorf("session limit %d exceeded", limits.MaxSessions)
}
sessions[row.SessionID] = true
}
rowBytes := corpusRowRetainedBytes(row)View on GitHub (pinned to 27d5a3981a)
Solutions
- Inspect the offending message and truncate or split oversized content/tool arguments before validation
- Raise limits.MaxMessageBytes in your CorpusLimits to fit the largest legitimate message, keeping the cap sane for your memory budget
- Pre-filter corpus rows with your own byte accounting (sum of len(role)+len(content)+sum of tool-call field lengths) before calling the API
- If the blob is legitimate payload, move it out of the corpus and reference it by hash/pointer instead
Example fix
// before
msg := CorpusMessage{Role: "user", Content: json.RawMessage(hugeJSON)}
err := validateCorpusMessage(msg, limits) // message exceeds byte limit
// after
if approxMessageBytes(msg) > limits.MaxMessageBytes {
msg.Content = json.RawMessage(truncateTo(hugeJSON, limits.MaxMessageBytes/2))
}
err := validateCorpusMessage(msg, limits) Defensive patterns
Strategy: validation
Validate before calling
func messageFits(msg CorpusMessage, limits CorpusLimits) bool {
n := len(msg.Role) + len(msg.Content)
for _, c := range msg.ToolCalls {
n += len(c.ID) + len(c.Type) + len(c.Function.Name) + len(c.Function.Arguments)
}
return n <= limits.MaxMessageBytes
}
for i := range corpus.Rows {
for _, m := range corpus.Rows[i].Messages {
if !messageFits(m, limits) { /* truncate or reject */ }
}
} Try / catch
if err := validateCorpus(msg, limits); err != nil {
if err.Error() == "message exceeds byte limit" {
// split or truncate the oversized message, revalidate
}
return err
} Prevention
- Set MaxMessageBytes from your real corpus's p99 message size plus headroom, not a guess
- Sum role+content+tool-call field lengths with the same formula the validator uses before submitting
- Reject or shard tool outputs that embed whole files/base64 blobs into arguments
When it happens
Trigger: Calling corpus validation (directly or via RunCorpus/BuildCorpusTrace) with a CorpusMessage whose combined len() of role, string content, and each tool call's ID/type/name/arguments sums above limits.MaxMessageBytes. Typically a message with a multi-megabyte content string or huge tool-call argument JSON.
Common situations: Importing a public agent trace that contains one oversized turn (e.g. a tool that dumped an entire file or base64 blob as arguments); setting MaxMessageBytes lower than the real corpus needs; miscounting bytes because tool arguments are counted raw, not compacted.
Related errors
- cachebench: nil corpus reader
- tool message requires tool_call_id
- cachebench: invalid corpus
- cachebench: no providers
- cachebench: corpus has no sessions
AI-assisted analysis of JuliusBrussee/caveman@27d5a3981a (2026-08-15).
Data as JSON: /api/errors/51000b28a2462662.
Report an issue: GitHub.