VectifyAI/PageIndex · error · PageIndexAPIError

Unsupported role for chat_completions: {role!r}. Tool histor

Error message

Unsupported role for chat_completions: {role!r}. Tool history round-trips belong to responses() or messages().

What it means

chat_completions only accepts the roles system, developer, user, and assistant. Any other role — notably 'tool' — is rejected with the offending role echoed, because tool-history round-trips are handled by responses() or messages().

Source

Thrown at pageindex/local_chat.py:82

    system_texts: list[str] = []
    history: list[dict] = []
    for message in messages:
        if not isinstance(message, dict) or "role" not in message:
            raise PageIndexAPIError(
                "Each message must be a dict with 'role' and 'content'.")
        role = message["role"]
        if role in ("system", "developer"):
            system_texts.append(_system_text(message.get("content")))
        elif role in ("user", "assistant"):
            content = message.get("content")
            if not isinstance(content, str):
                raise PageIndexAPIError(
                    "chat_completions content must be a string; for "
                    "structured items use responses() or messages()."
                )
            history.append({"role": role, "content": content})
        else:
            raise PageIndexAPIError(
                f"Unsupported role for chat_completions: {role!r}. Tool "
                "history round-trips belong to responses() or messages()."
            )
    if not history:
        raise PageIndexAPIError("messages must contain a user or assistant "
                                "message.")
    return system_texts, history


def _run_sync(coro):
    from .utils import run_off_loop
    return run_off_loop(asyncio.run, coro)


_SENTINEL = object()


def _stream_sync(agen_factory) -> Iterator[Any]:

View on GitHub (pinned to afb5e11976)

Solutions

  1. Filter out non-supported roles before calling: keep only system/developer/user/assistant
  2. Use responses() or messages() for transcripts with tool history
  3. Check role strings against a whitelist when constructing messages

Example fix

# before
client.chat_completions(model=m, messages=full_transcript)

# after
msgs=[m for m in full_transcript if m["role"] in {"system","developer","user","assistant"}]
client.chat_completions(model=m, messages=msgs)
Defensive patterns

Strategy: validation

Validate before calling

ALLOWED = {'system','developer','user','assistant'}
messages = [m for m in messages if m.get('role') in ALLOWED]

Type guard

def is_supported_role(role) -> bool:
    return role in {'system','developer','user','assistant'}

Try / catch

null

Prevention

When it happens

Trigger: messages containing {"role":"tool","content":...} or a typo'd role like "assitant"/"systemn".

Common situations: Feeding full agent transcripts (which include tool results) back into chat_completions, role typos, converting between Anthropic and OpenAI role vocabularies (e.g. 'tool_result').

Related errors


AI-assisted analysis of VectifyAI/PageIndex@afb5e11976 (2026-08-27). Data as JSON: /api/errors/7384607ed52276e5. Report an issue: GitHub.