Skip to content

fix: validate message roles early to prevent 500 stack trace leakage#31409

Closed
xbrxr03 wants to merge 1 commit into
BerriAI:litellm_internal_stagingfrom
xbrxr03:fix/role-validation-clean
Closed

fix: validate message roles early to prevent 500 stack trace leakage#31409
xbrxr03 wants to merge 1 commit into
BerriAI:litellm_internal_stagingfrom
xbrxr03:fix/role-validation-clean

Conversation

@xbrxr03

@xbrxr03 xbrxr03 commented Jun 26, 2026

Copy link
Copy Markdown

Problem

When a chat completion request is sent with an invalid role value (e.g. "admin", "xyz"), litellm leaks a full Python stack trace in the error response body, including internal file paths and exception chains. The response also returns HTTP 500 instead of the correct 400.

Example error response from the bug:

{
  "error": {
    "message": "litellm.APIConnectionError: Invalid Message passed in - {\"role\": \"xyz\", ...}. File an issue...\nTraceback (most recent call last):\n  ...",
    "type": "500"
  }
}

Fix

Adds early role validation in validate_and_fix_openai_messages() that raises BadRequestError (HTTP 400) with a sanitized message for invalid roles. Valid roles (system, user, assistant, tool, function, developer) continue to work. Missing roles still default to assistant (existing behavior preserved).

3 files changed, 89 insertions:

  • litellm/constants.py — add VALID_MESSAGE_ROLES set
  • litellm/utils.py — validate role in validate_and_fix_openai_messages()
  • tests/litellm_utils_tests/test_invalid_role_validation.py — 5 tests

Fixes #30948


This PR was created with AI assistance (OpenClaw DEV agent). Per the project's AI contribution guidelines, this addresses a confirmed bug from an open issue.

When an invalid role (e.g. 'admin', 'xyz') is sent in a chat completion
request, litellm currently returns a 500 with a full Python stack trace.
This validates roles early in validate_and_fix_openai_messages() and
raises BadRequestError(400) with a sanitized message instead.

Fixes BerriAI#30948

This PR is submitted with AI assistance (OpenClaw DEV agent). Per the
project's AI policy, this addresses a confirmed bug from an open issue.
@greptile-apps

greptile-apps Bot commented Jun 26, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR adds early validation of message role values in validate_and_fix_openai_messages(), raising BadRequestError (HTTP 400) for any role not in a fixed allow-list instead of propagating a 500 stack trace. A VALID_MESSAGE_ROLES constant is added to constants.py and five unit tests are included.

  • The validation fires in litellm/main.py at the very top of completion(), before any provider-specific message transformation, which means provider-specific roles supported by non-OpenAI backends will now receive a 400 rejection they cannot work around.
  • The error message interpolates sorted(VALID_MESSAGE_ROLES) as a Python list literal; a joined string would be cleaner for end-users.
  • The test_valid_roles_still_work test only asserts len(result) == 1, not that the role value is preserved unchanged.

Confidence Score: 3/5

The change hardens a real error path but introduces a new hard rejection at the top of completion() that affects all providers — custom or provider-specific roles that worked before will now always return a 400 with no escape hatch.

The validation is placed before provider-specific message transformation in the central completion() entry point, meaning any role outside the hard-coded set is permanently blocked for every provider integration, not just OpenAI.

litellm/utils.py — the placement and scope of the role check needs a second look before merging.

Important Files Changed

Filename Overview
litellm/constants.py Adds VALID_MESSAGE_ROLES set constant — straightforward and follows the existing pattern of centralizing constants.
litellm/utils.py Adds early role validation in validate_and_fix_openai_messages; the check fires before any provider-specific transformation in completion(), which will now reject non-OpenAI roles that were previously forwarded to the provider.
tests/litellm_utils_tests/test_invalid_role_validation.py New unit tests for role validation; tests use mocked/unit-level calls (no real network calls) and cover core invalid/valid/missing-role cases, though the valid-role assertion is minimal.

Reviews (1): Last reviewed commit: "fix: validate message roles early to pre..." | Re-trigger Greptile

Comment thread litellm/utils.py
Comment on lines +8237 to +8242
elif message["role"] not in VALID_MESSAGE_ROLES:
raise BadRequestError(
message=f"Invalid role: '{message['role']}'. Supported roles are: {sorted(VALID_MESSAGE_ROLES)}",
model="",
llm_provider="",
)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Early rejection blocks provider-specific roles

validate_and_fix_openai_messages is called in litellm/main.py at line 5055 — very early in completion(), before any provider-specific message transformation runs. Some open-source/self-hosted backends (e.g., Ollama serving Llama 3.x) accept roles beyond the OpenAI set (like "ipython" for tool-call results). Those messages are now blocked with a 400 before they ever reach the provider transformation layer, whereas before they were forwarded and the provider decided. Users currently passing such custom roles will see a new failure they cannot work around without litellm changes.

Comment thread litellm/utils.py
Comment on lines +8238 to +8242
raise BadRequestError(
message=f"Invalid role: '{message['role']}'. Supported roles are: {sorted(VALID_MESSAGE_ROLES)}",
model="",
llm_provider="",
)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 The error message interpolates sorted(VALID_MESSAGE_ROLES) which produces a Python list literal (e.g., ['assistant', 'developer', ...]). A comma-separated string is cleaner for a user-facing error.

Suggested change
raise BadRequestError(
message=f"Invalid role: '{message['role']}'. Supported roles are: {sorted(VALID_MESSAGE_ROLES)}",
model="",
llm_provider="",
)
raise BadRequestError(
message=f"Invalid role: '{message['role']}'. Supported roles are: {', '.join(sorted(VALID_MESSAGE_ROLES))}",
model="",
llm_provider="",
)

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Comment on lines +53 to +62
for role in valid_roles:
messages = [{"role": role, "content": "test"}]
result = validate_and_fix_openai_messages(messages=messages)
assert len(result) == 1

def test_invalid_role_error_no_stack_trace(self):
"""The error response body must not contain Python internal paths or tracebacks."""
from litellm.utils import validate_and_fix_openai_messages

messages = [{"role": "hacker", "content": "pwned"}]

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 tool and function role test assertions are too weak

validate_and_fix_openai_messages calls validate_chat_completion_user_messages on the result. The test only asserts len(result) == 1 and does not verify the role is preserved, so it would pass even if the role were silently mutated. Additionally, tool messages typically require a tool_call_id field; the bare {"role": "tool", "content": "test"} fixture may silently pass today but break if stricter downstream validation is added.

@CLAassistant

Copy link
Copy Markdown

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.
You have signed the CLA already but the status is still pending? Let us recheck it.

@codecov

codecov Bot commented Jun 26, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 66.66667% with 1 line in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
litellm/utils.py 50.00% 1 Missing ⚠️

📢 Thoughts on this report? Let us know!

@xbrxr03

xbrxr03 commented Jul 2, 2026

Copy link
Copy Markdown
Author

Closing due to lack of maintainer response. Happy to reopen if there's interest. Thanks for the review opportunity.

@xbrxr03 xbrxr03 closed this Jul 2, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: chat completion API is Vulnerable to Stack Trace & Internal Path Disclosure via Improper Error Handling

3 participants