{"record":{"id":"968adf54f20df84f","repo":"JuliusBrussee/caveman","slug":"message-d-w","errorCode":null,"errorMessage":"message %d: %w","messagePattern":"message (.+?): %w","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cacheengine/cachebench/corpus.go","lineNumber":337,"sourceCode":"\tif wire.OutputLength < 0 || math.IsNaN(wire.PreGap) || math.IsInf(wire.PreGap, 0) || wire.PreGap < 0 || wire.PreGap > maxCorpusGapSeconds {\n\t\treturn CorpusRow{}, fmt.Errorf(\"output_length must be non-negative and pre_gap must be within 0..%d seconds\", maxCorpusGapSeconds)\n\t}\n\tif len(wire.Input) == 0 || !json.Valid(wire.Input) {\n\t\treturn CorpusRow{}, errors.New(\"input must be valid non-empty message array\")\n\t}\n\tif len(wire.SessionID)+len(wire.Model)+len(wire.Input) > limits.MaxRowBytes {\n\t\treturn CorpusRow{}, fmt.Errorf(\"row exceeds byte limit %d\", limits.MaxRowBytes)\n\t}\n\tvar messages []CorpusMessage\n\tif err := json.Unmarshal(wire.Input, &messages); err != nil {\n\t\treturn CorpusRow{}, fmt.Errorf(\"decode input: %w\", err)\n\t}\n\tif len(messages) == 0 || len(messages) > limits.MaxMessagesPerRequest {\n\t\treturn CorpusRow{}, fmt.Errorf(\"message count %d outside 1..%d\", len(messages), limits.MaxMessagesPerRequest)\n\t}\n\tfor messageIndex, message := range messages {\n\t\tif err := validateCorpusMessage(message, limits); err != nil {\n\t\t\treturn CorpusRow{}, fmt.Errorf(\"message %d: %w\", messageIndex, err)\n\t\t}\n\t}\n\treturn CorpusRow{\n\t\tRowIndex: index, SessionID: wire.SessionID, Model: wire.Model, Input: messages,\n\t\tOutputLength: wire.OutputLength, PreGap: wire.PreGap,\n\t}, nil\n}\n\nfunc validateCorpusMessage(message CorpusMessage, limits CorpusLimits) error {\n\tswitch message.Role {\n\tcase \"system\", \"developer\", \"user\", \"assistant\", \"tool\":\n\tdefault:\n\t\treturn fmt.Errorf(\"unsupported role %q\", message.Role)\n\t}\n\tif !validBoundedText(message.ToolCallID, 2048, true) || !validBoundedText(message.Name, 512, true) {\n\t\treturn errors.New(\"invalid message identity\")\n\t}\n\tif len(message.Content) == 0 && len(message.ToolCalls) == 0 {","sourceCodeStart":319,"sourceCodeEnd":355,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/27d5a3981a347890211bb1bf2439e5c821a63bc9/cacheengine/cachebench/corpus.go#L319-L355","documentation":"A corpus row's input decoded successfully as JSON, but one of its messages failed per-message validation in validateCorpusMessage. The error wraps the underlying cause with the zero-based message index, so '%w' reveals the real problem (bad role, oversized content, missing tool_call_id, etc.). It is returned while converting a wire-format row into a CorpusRow, before the row ever enters the benchmark.","triggerScenarios":"Calling the corpus row decode path (LoadCorpus / row conversion) where row.Input unmarshals to a []CorpusMessage whose element i violates validateCorpusMessage: unsupported role, invalid tool_call_id/name bounds, empty content and tool_calls, content over MaxMessageBytes or not valid JSON, or a tool message without tool_call_id.","commonSituations":"Hand-edited or generated corpus JSONL with a typo'd role ('Tool', 'function'), assistant messages with neither content nor tool_calls, truncated content strings that break JSON validity, or a corpus produced for different CorpusLimits than the ones configured now.","solutions":["Read the wrapped '%w' clause — it names the exact rule broken; fix that field in the offending message.","Check message index reported by '%d' against the row's input array to locate the bad entry.","If the corpus is legitimately larger, raise the relevant limit in CorpusLimits (MaxMessageBytes) and re-run.","Re-emit the corpus from its source (export path) instead of hand-editing JSONL.","Add a pre-flight validator over the corpus file before benchmark runs."],"exampleFix":"// before (corpus row input)\n{\"role\":\"Tool\",\"content\":\"result\"}\n// after\n{\"role\":\"tool\",\"tool_call_id\":\"call_1\",\"content\":\"result\"}","handlingStrategy":"validation","validationCode":"func preflightRow(input json.RawMessage, limits CorpusLimits) error {\n\tvar msgs []CorpusMessage\n\tif err := json.Unmarshal(input, &msgs); err != nil {\n\t\treturn fmt.Errorf(\"decode input: %w\", err)\n\t}\n\tif len(msgs) == 0 || len(msgs) > limits.MaxMessagesPerRequest {\n\t\treturn fmt.Errorf(\"message count %d outside 1..%d\", len(msgs), limits.MaxMessagesPerRequest)\n\t}\n\tfor i, m := range msgs {\n\t\tif err := validateCorpusMessage(m, limits); err != nil {\n\t\t\treturn fmt.Errorf(\"message %d: %w\", i, err)\n\t\t}\n\t}\n\treturn nil\n}","typeGuard":null,"tryCatchPattern":"if err := corpus.LoadCorpus(path, limits); err != nil {\n\tvar msgErr *fmt.wrapError // inspect wrapped cause via errors.Unwrap\n\t_ = msgErr\n\tlog.Fatalf(\"corpus load failed: %v\", err)\n}","preventionTips":["Run validateCorpusMessage over all rows before starting a benchmark run.","Generate corpora only through the tool's export path; never hand-edit JSONL.","Pin CorpusLimits alongside the corpus file so limits travel with the data."],"tags":["go","json","validation","corpus","cachebench"],"backgroundTag":null,"analyzedSha":"27d5a3981a347890211bb1bf2439e5c821a63bc9","analyzedAt":"2026-08-15T09:26:11.751Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}