microsoft/graphrag · error · ValueError

Invalid Role: {value}

Error message

Invalid Role: {value}

What it means

ConversationRole.from_string only accepts the exact lowercase strings 'user' and 'assistant'. Any other value (including 'USER', 'system', 'tool', or None) raises ValueError with the offending value. This is strict enum parsing for LLM conversation history in GraphRAG queries.

Source

Thrown at packages/graphrag/graphrag/query/context_builder/conversation_history.py:37

class ConversationRole(str, Enum):
    """Enum for conversation roles."""

    SYSTEM = "system"
    USER = "user"
    ASSISTANT = "assistant"

    @staticmethod
    def from_string(value: str) -> "ConversationRole":
        """Convert string to ConversationRole."""
        if value == "system":
            return ConversationRole.SYSTEM
        if value == "user":
            return ConversationRole.USER
        if value == "assistant":
            return ConversationRole.ASSISTANT

        msg = f"Invalid Role: {value}"
        raise ValueError(msg)

    def __str__(self) -> str:
        """Return string representation of the enum value."""
        return self.value


"""
Data class for storing a single conversation turn
"""


@dataclass
class ConversationTurn:
    """Data class for storing a single conversation turn."""

    role: ConversationRole
    content: str

View on GitHub (pinned to f40e9a26ce)

Solutions

  1. Normalize roles to lowercase 'user'/'assistant' before calling from_list
  2. Filter out or remap non-chat roles (system/tool/function) to 'user' or drop them
  3. Inspect the offending message with a quick print to see the exact bad value in the error

Example fix

# before
history = ConversationHistory.from_list([{'role': 'System', 'content': '...'}])
# after
msgs = [{'role': m['role'].lower(), 'content': m['content']}
        for m in raw if m['role'].lower() in ('user', 'assistant')]
history = ConversationHistory.from_list(msgs)
Defensive patterns

Strategy: validation

Validate before calling

VALID = {'user', 'assistant'}
msgs = [m for m in raw if str(m.get('role', '')).lower() in VALID]
history = ConversationHistory.from_list(msgs)

Type guard

def is_valid_role(r: object) -> bool:
    return isinstance(r, str) and r.lower() in ('user', 'assistant')

Try / catch

try:
    history = ConversationHistory.from_list(msgs)
except ValueError as e:
    logger.warning('dropping bad role: %s', e)
    history = ConversationHistory()

Prevention

When it happens

Trigger: Calling ConversationHistory.from_list / from_string with messages whose 'role' field isn't exactly 'user' or 'assistant' — e.g. role='System', role='system', role=None, or a typo like 'asistant'.

Common situations: Feeding OpenAI-style chat history containing 'system' or 'tool' roles; loading stored conversation JSON where roles were capitalized or localized; hand-built history dicts in notebooks.

Related errors


AI-assisted analysis of microsoft/graphrag@f40e9a26ce (2026-08-27). Data as JSON: /api/errors/c812255cdfd8432d. Report an issue: GitHub.