{"record":{"id":"786e1fca7185d01e","repo":"googleapis/mcp-toolbox","slug":"invalid-role-q-must-be-user-or-assistant","errorCode":null,"errorMessage":"invalid role %q: must be 'user' or 'assistant'","messagePattern":"invalid role %q: must be 'user' or 'assistant'","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/prompts/messages.go","lineNumber":48,"sourceCode":"\tuserRole      = \"user\"\n\tassistantRole = \"assistant\"\n)\n\nfunc (m *Message) UnmarshalYAML(unmarshal func(interface{}) error) error {\n\t// Use a type alias to prevent an infinite recursion loop. The alias\n\t// has the same fields but lacks the UnmarshalYAML method.\n\ttype messageAlias Message\n\tvar alias messageAlias\n\tif err := unmarshal(&alias); err != nil {\n\t\treturn err\n\t}\n\n\t*m = Message(alias)\n\tif m.Role == \"\" {\n\t\tm.Role = userRole\n\t}\n\tif m.Role != userRole && m.Role != assistantRole {\n\t\treturn fmt.Errorf(\"invalid role %q: must be 'user' or 'assistant'\", m.Role)\n\t}\n\treturn nil\n}\n\n// SubstituteMessages takes a slice of Messages and a set of parameter values,\n// and returns a new slice with all template variables resolved.\nfunc SubstituteMessages(messages []Message, arguments Arguments, argValues parameters.ParamValues) ([]Message, error) {\n\tsubstitutedMessages := make([]Message, 0, len(messages))\n\targsMap := argValues.AsMap()\n\n\tvar params parameters.Parameters\n\tfor _, arg := range arguments {\n\t\tparams = append(params, arg.Parameter)\n\t}\n\n\tfor _, msg := range messages {\n\t\tsubstitutedContent, err := parameters.ResolveTemplateParams(params, msg.Content, argsMap)\n\t\tif err != nil {","sourceCodeStart":30,"sourceCodeEnd":66,"githubUrl":"https://github.com/googleapis/mcp-toolbox/blob/8cc6e09de2ad7b8bffc77751799585a1401a48eb/internal/prompts/messages.go#L30-L66","documentation":"The custom UnmarshalYAML for prompt Messages validates that each message's `role` is either \"user\" or \"assistant\" (empty defaults to \"user\"). It throws this error when a prompt YAML specifies any other role value.","triggerScenarios":"A prompt definition includes a message with role like \"system\", \"tool\", \"function\", a typo (\"usre\"), or different casing (\"User\"), which fails the strict equality check.","commonSituations":"Porting prompts from other LLM frameworks that allow \"system\" roles; copying ChatML/other schemas; capitalization inconsistencies in hand-written YAML.","solutions":["Change the role to exactly `user` or `assistant` (lowercase).","Remove the role field entirely if user is intended — it defaults to user.","Fold system-style instructions into the first `user` message content.","Check casing: \"User\" is invalid; only lowercase works."],"exampleFix":"// before\nmessages:\n  - role: system\n    content: You are helpful.\n// after\nmessages:\n  - role: user\n    content: You are helpful. (fold system text into user message)","handlingStrategy":"validation","validationCode":"validRoles := map[string]bool{\"user\": true, \"assistant\": true}\nfor _, m := range cfg.Messages {\n\tif m.Role != \"\" && !validRoles[m.Role] {\n\t\treturn fmt.Errorf(\"prompt message role %q invalid; use user|assistant\", m.Role)\n\t}\n}","typeGuard":"func isValidPromptRole(r string) bool {\n\treturn r == \"\" || r == \"user\" || r == \"assistant\"\n}","tryCatchPattern":"if err := yaml.Unmarshal(data, &cfg); err != nil {\n\tif strings.Contains(err.Error(), \"invalid role\") {\n\t\treturn fmt.Errorf(\"prompt messages only support role 'user' or 'assistant' (omitted defaults to user)\")\n\t}\n\treturn err\n}","preventionTips":["Omit `role` for user messages; only specify 'assistant' when needed.","Never copy 'system'/'tool' roles from other frameworks into prompt YAML.","Keep roles lowercase; validate prompts in CI with a JSON/YAML schema."],"tags":["yaml","prompts","validation","configuration"],"backgroundTag":"invalid-role-value","analyzedSha":"8cc6e09de2ad7b8bffc77751799585a1401a48eb","analyzedAt":"2026-09-05T01:10:36.887Z","contentChangedAt":"2026-09-05T01:10:36.887Z","schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}