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

  1. Inspect the offending message and truncate or split oversized content/tool arguments before validation
  2. Raise limits.MaxMessageBytes in your CorpusLimits to fit the largest legitimate message, keeping the cap sane for your memory budget
  3. 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
  4. 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

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


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