JuliusBrussee/caveman · error

retained corpus byte limit %d exceeded

Error message

retained corpus byte limit %d exceeded

What it means

The byte budget for retained corpus data is exhausted: corpusRowRetainedBytes(row) exceeds limits.MaxRetainedBytes minus what has already been accumulated. This bounds total memory held by the corpus, independent of the per-row and session-count caps.

Source

Thrown at cacheengine/cachebench/corpus.go:400

	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)
	if rowBytes > limits.MaxRetainedBytes-*retainedBytes {
		return fmt.Errorf("retained corpus byte limit %d exceeded", limits.MaxRetainedBytes)
	}
	*retainedBytes += rowBytes
	*rows = append(*rows, row)
	return nil
}

func corpusRetainedBytes(rows []CorpusRow) int64 {
	var total int64
	for _, row := range rows {
		total += corpusRowRetainedBytes(row)
	}
	return total
}

func corpusRowRetainedBytes(row CorpusRow) int64 {
	total := int64(len(row.SessionID) + len(row.Model))
	for _, message := range row.Input {
		total += int64(len(message.Role) + len(message.Content) + len(message.ToolCallID) + len(message.Name))

View on GitHub (pinned to 27d5a3981a)

Solutions

  1. Raise CorpusLimits.MaxRetainedBytes to fit the corpus's total retained size.
  2. Trim oversized messages in the corpus (shorten large tool outputs) before loading.
  3. Reduce MaxRows so the row cap binds before the byte cap.
  4. Shard the corpus and evaluate shards in separate runs.

Example fix

// before
limits.MaxRetainedBytes = 256 << 20
// after
limits.MaxRetainedBytes = 2 << 30
Defensive patterns

Strategy: validation

Validate before calling

// Estimate retained bytes from file size as a lower bound before loading:
func budgetCheck(path string, limits CorpusLimits) error {
	st, err := os.Stat(path)
	if err != nil {
		return err
	}
	if st.Size() > limits.MaxRetainedBytes {
		return fmt.Errorf("file size %d already exceeds MaxRetainedBytes %d", st.Size(), limits.MaxRetainedBytes)
	}
	return nil
}

Prevention

When it happens

Trigger: Cumulative retained bytes across already-appended rows plus the incoming row's retained size would exceed CorpusLimits.MaxRetainedBytes during appendCorpusRow.

Common situations: Loading a corpus with very large message contents (long system prompts, big tool payloads) under a default memory budget; raising MaxRows without proportionally raising MaxRetainedBytes.

Related errors


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