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

  1. Remove name from non-tool messages; prefix the identity into content instead (e.g. 'Alice: hi').
  2. For tool results, ensure role="tool" with both tool_call_id and, if used, name.
  3. 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

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


AI-assisted analysis of unslothai/unsloth@203007d190 (2026-08-15). Data as JSON: /api/errors/0aa381d1f8f57fd0. Report an issue: GitHub.