{"record":{"id":"2ab9ebf5c5398684","repo":"JuliusBrussee/caveman","slug":"retained-corpus-byte-limit-d-exceeded","errorCode":null,"errorMessage":"retained corpus byte limit %d exceeded","messagePattern":"retained corpus byte limit (.+?) exceeded","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cacheengine/cachebench/corpus.go","lineNumber":400,"sourceCode":"\tif message.Role == \"tool\" && message.ToolCallID == \"\" {\n\t\treturn errors.New(\"tool message requires tool_call_id\")\n\t}\n\treturn nil\n}\n\nfunc appendCorpusRow(rows *[]CorpusRow, sessions map[string]bool, retainedBytes *int64, row CorpusRow, limits CorpusLimits) error {\n\tif len(*rows) >= limits.MaxRows {\n\t\treturn fmt.Errorf(\"row limit %d exceeded\", limits.MaxRows)\n\t}\n\tif !sessions[row.SessionID] {\n\t\tif len(sessions) >= limits.MaxSessions {\n\t\t\treturn fmt.Errorf(\"session limit %d exceeded\", limits.MaxSessions)\n\t\t}\n\t\tsessions[row.SessionID] = true\n\t}\n\trowBytes := corpusRowRetainedBytes(row)\n\tif rowBytes > limits.MaxRetainedBytes-*retainedBytes {\n\t\treturn fmt.Errorf(\"retained corpus byte limit %d exceeded\", limits.MaxRetainedBytes)\n\t}\n\t*retainedBytes += rowBytes\n\t*rows = append(*rows, row)\n\treturn nil\n}\n\nfunc corpusRetainedBytes(rows []CorpusRow) int64 {\n\tvar total int64\n\tfor _, row := range rows {\n\t\ttotal += corpusRowRetainedBytes(row)\n\t}\n\treturn total\n}\n\nfunc corpusRowRetainedBytes(row CorpusRow) int64 {\n\ttotal := int64(len(row.SessionID) + len(row.Model))\n\tfor _, message := range row.Input {\n\t\ttotal += int64(len(message.Role) + len(message.Content) + len(message.ToolCallID) + len(message.Name))","sourceCodeStart":382,"sourceCodeEnd":418,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/27d5a3981a347890211bb1bf2439e5c821a63bc9/cacheengine/cachebench/corpus.go#L382-L418","documentation":"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.","triggerScenarios":"Cumulative retained bytes across already-appended rows plus the incoming row's retained size would exceed CorpusLimits.MaxRetainedBytes during appendCorpusRow.","commonSituations":"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.","solutions":["Raise CorpusLimits.MaxRetainedBytes to fit the corpus's total retained size.","Trim oversized messages in the corpus (shorten large tool outputs) before loading.","Reduce MaxRows so the row cap binds before the byte cap.","Shard the corpus and evaluate shards in separate runs."],"exampleFix":"// before\nlimits.MaxRetainedBytes = 256 << 20\n// after\nlimits.MaxRetainedBytes = 2 << 30","handlingStrategy":"validation","validationCode":"// Estimate retained bytes from file size as a lower bound before loading:\nfunc budgetCheck(path string, limits CorpusLimits) error {\n\tst, err := os.Stat(path)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif st.Size() > limits.MaxRetainedBytes {\n\t\treturn fmt.Errorf(\"file size %d already exceeds MaxRetainedBytes %d\", st.Size(), limits.MaxRetainedBytes)\n\t}\n\treturn nil\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Size MaxRetainedBytes from the corpus file size plus overhead, not guesswork.","Scale MaxRetainedBytes whenever MaxRows is raised.","Strip huge tool outputs from corpora before benchmarking."],"tags":["go","limits","memory","corpus"],"backgroundTag":null,"analyzedSha":"27d5a3981a347890211bb1bf2439e5c821a63bc9","analyzedAt":"2026-08-15T09:26:11.751Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}