{"record":{"id":"c235e294444bf7fa","repo":"JuliusBrussee/caveman","slug":"unsupported-role-q","errorCode":null,"errorMessage":"unsupported role %q","messagePattern":"unsupported role %q","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cacheengine/cachebench/corpus.go","lineNumber":350,"sourceCode":"\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 {\n\t\treturn errors.New(\"content or tool_calls required\")\n\t}\n\tif len(message.Content) > limits.MaxMessageBytes || (len(message.Content) > 0 && !json.Valid(message.Content)) {\n\t\treturn errors.New(\"content invalid or exceeds limit\")\n\t}\n\tmessageBytes := len(message.Content) + len(message.Role) + len(message.ToolCallID) + len(message.Name)\n\tif len(message.Content) > 0 {\n\t\tvar content any\n\t\tif err := json.Unmarshal(message.Content, &content); err != nil {\n\t\t\treturn errors.New(\"content must decode\")\n\t\t}\n\t\tif content != nil {\n\t\t\tif _, ok := content.(string); !ok {","sourceCodeStart":332,"sourceCodeEnd":368,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/27d5a3981a347890211bb1bf2439e5c821a63bc9/cacheengine/cachebench/corpus.go#L332-L368","documentation":"A corpus message carried a role outside the fixed allow-list 'system', 'developer', 'user', 'assistant', 'tool'. The switch's default branch rejects anything else, including case variants and legacy role names, because downstream prefix/cache logic only reasons about these five roles.","triggerScenarios":"validateCorpusMessage runs over each message of a decoded corpus row; any message whose Role string is not exactly one of the five allowed values returns this error, which then surfaces wrapped as 'message %d: unsupported role %q'.","commonSituations":"Corpora exported from older OpenAI formats using 'function' role, capitalized 'User'/'Assistant', or custom roles injected by middleware; sed/regex edits that mangle the role field.","solutions":["Set the role to one of the five exact lowercase strings; map legacy 'function' to 'tool'.","Re-export the corpus with a role-normalization step.","Drop or rewrite messages with custom roles before loading if they are not needed for the cache benchmark."],"exampleFix":"// before\n{\"role\":\"function\",\"content\":\"...\"}\n// after\n{\"role\":\"tool\",\"tool_call_id\":\"call_1\",\"content\":\"...\"}","handlingStrategy":"validation","validationCode":"var allowedRoles = map[string]bool{\"system\": true, \"developer\": true, \"user\": true, \"assistant\": true, \"tool\": true}\n\nfunc normalizeRole(role string) (string, error) {\n\tswitch strings.ToLower(strings.TrimSpace(role)) {\n\tcase \"function\":\n\t\treturn \"tool\", nil\n\tcase \"system\", \"developer\", \"user\", \"assistant\", \"tool\":\n\t\treturn strings.ToLower(strings.TrimSpace(role)), nil\n\tdefault:\n\t\treturn \"\", fmt.Errorf(\"unsupported role %q\", role)\n\t}\n}","typeGuard":"func isValidRole(role string) bool {\n\tswitch role {\n\tcase \"system\", \"developer\", \"user\", \"assistant\", \"tool\":\n\t\treturn true\n\t}\n\treturn false\n}","tryCatchPattern":"if err := validateCorpusMessage(msg, limits); err != nil {\n\tif strings.Contains(err.Error(), \"unsupported role\") {\n\t\tmsg.Role, err = normalizeRole(msg.Role)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}","preventionTips":["Normalize roles at corpus export time.","Add a role allow-list assertion in corpus generation tests.","Watch for 'function' role in corpora converted from legacy OpenAI captures."],"tags":["go","validation","corpus","roles"],"backgroundTag":null,"analyzedSha":"27d5a3981a347890211bb1bf2439e5c821a63bc9","analyzedAt":"2026-08-15T09:26:11.751Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}