unslothai/unsloth · error · ValueError
"name" is only valid on role="tool" messages.
Error message
"name" is only valid on role="tool" messages.
What it means
Raised by the ChatMessage model validator when the name field is set on a message whose role is not "tool". In the OpenAI schema, name on a message identifies which tool produced a tool result; on other roles it is not accepted by this server.
Source
Thrown at studio/backend/models/inference.py:1260
),
)
@field_validator("reasoning_content", mode = "before")
@classmethod
def _ignore_non_string_reasoning(cls, value):
# This field used to be ignored as an unknown key. Some compatible
# gateways send structured reasoning, so declaring the string form must
# not turn those previously accepted requests into validation errors.
return value if isinstance(value, str) else None
@model_validator(mode = "after")
def _validate_role_shape(self) -> "ChatMessage":
if self.tool_calls is not None and self.role != "assistant":
raise ValueError('"tool_calls" is only valid on role="assistant" messages.')
if self.tool_call_id is not None and self.role != "tool":
raise ValueError('"tool_call_id" is only valid on role="tool" messages.')
if self.name is not None and self.role != "tool":
raise ValueError('"name" is only valid on role="tool" messages.')
if self.role == "tool":
# tool_call_id resolution happens at ChatCompletionRequest scope.
# OpenAI accepts empty tool results (commands with no output);
# normalize to "" instead of a 400 agentic clients treat as fatal.
if self.content is None or self.content == []:
self.content = ""
elif self.role == "assistant":
# Post-Stop sentinel: collapse content="" / [] to None.
if (self.content == "" or self.content == []) and not self.tool_calls:
self.content = None
else: # "user" | "system"
if self.content is None or self.content == []:
raise ValueError(f'role="{self.role}" messages require "content".')
return self
class ThinkingConfig(BaseModel):View on GitHub (pinned to 203007d190)
Solutions
- Remove name from non-tool messages; prefix the identity into content instead (e.g. 'Alice: hi').
- For tool results, ensure role="tool" with both tool_call_id and, if used, name.
- Update to a schema where only role="tool" messages carry name.
Example fix
# before
{"role": "user", "name": "alice", "content": "hello"}
# after
{"role": "user", "content": "alice: hello"} Defensive patterns
Strategy: type-guard
Type guard
def message_ok(msg: dict) -> bool:
if msg.get('name') is not None:
return msg.get('role') == 'tool'
return True Prevention
- Drop the legacy name-on-user/system pattern; inline identity into content
- Keep a schema (pydantic/OpenAPI) on the client and validate before send
- Watch for SDK changes that stop tolerating name on non-tool roles
When it happens
Trigger: Sending {"role": "user", "name": "Alice", "content": "hi"} to /v1/chat/completions — some clients still send the legacy name-on-user-message pattern. Also {"role": "system", "name": "persona"} from older OpenAI examples.
Common situations: Legacy OpenAI API examples that named user/system messages; multi-user chat front-ends forwarding a username field; SDK versions where name was tolerated on all roles and a strict update now rejects it.
Related errors
- "tool_calls" is only valid on role="assistant" messages.
- "tool_call_id" is only valid on role="tool" messages.
- role="{self.role}" messages require "content".
- Provide either content_base64 or file_ids, not both
- Provide either content_base64 or file_ids
AI-assisted analysis of unslothai/unsloth@203007d190 (2026-08-15).
Data as JSON: /api/errors/0aa381d1f8f57fd0.
Report an issue: GitHub.