BerriAI/litellm · error · BadRequestError
BedrockException - {error_str}\n. Pass in default user messa
Error message
BedrockException - {error_str}\n. Pass in default user message via `completion(..,user_continue_message=)` or enable `litellm.modify_params=True`.\nFor Proxy: do via `litellm_settings::modify_params: True` or user_continue_message under `litellm_params` What it means
LiteLLM throws BadRequestError when Bedrock Converse rejects a conversation whose first message is not from the user (typically it starts with an assistant or tool message). The mapper's message names the two supported remedies: pass user_continue_message=... to completion() so LiteLLM inserts a default user message, or enable litellm.modify_params=True (proxy: litellm_settings::modify_params: True). It is purely a message-ordering constraint of the Converse API, not a size or auth problem.
Source
Thrown at litellm/litellm_core_utils/exception_mapping_utils.py:852
model=model,
llm_provider="bedrock",
)
elif "Conversation blocks and tool result blocks cannot be provided in the same turn." in error_str:
raise BadRequestError(
message=f"BedrockException - {error_str}\n. Enable 'litellm.modify_params=True' (for PROXY do: `litellm_settings::modify_params: True`) to insert a dummy assistant message and fix this error.",
model=model,
llm_provider="bedrock",
response=getattr(original_exception, "response", None),
)
elif "Malformed input request" in error_str:
raise BadRequestError(
message=f"BedrockException - {error_str}",
model=model,
llm_provider="bedrock",
response=getattr(original_exception, "response", None),
)
elif "A conversation must start with a user message." in error_str:
raise BadRequestError(
message=f"BedrockException - {error_str}\n. Pass in default user message via `completion(..,user_continue_message=)` or enable `litellm.modify_params=True`.\nFor Proxy: do via `litellm_settings::modify_params: True` or user_continue_message under `litellm_params`",
model=model,
llm_provider="bedrock",
response=getattr(original_exception, "response", None),
)
elif (
"Unable to locate credentials" in error_str
or "The security token included in the request is invalid" in error_str
):
raise AuthenticationError(
message=f"BedrockException Invalid Authentication - {error_str}",
model=model,
llm_provider="bedrock",
response=getattr(original_exception, "response", None),
)
elif "AccessDeniedException" in error_str:
raise PermissionDeniedError(
message=f"BedrockException PermissionDeniedError - {error_str}",View on GitHub (pinned to 77b7c6c40c)
Solutions
- Pass user_continue_message='...' (or litellm_params user_continue_message on the proxy) so a default user message is prepended
- Enable litellm.modify_params=True in code, or litellm_settings::modify_params: True on the proxy
- Reorder locally: ensure messages[0]['role'] == 'user' before calling (prepend a user turn or drop leading assistant turns)
- When trimming history, always keep or synthesize a leading user message
Example fix
# before
messages = [{'role': 'assistant', 'content': 'Continuing earlier answer...'}, ...]
resp = litellm.completion(model="bedrock/...", messages=messages)
# after
messages = [{'role': 'user', 'content': 'Continue, please.'}] + messages
resp = litellm.completion(model="bedrock/...", messages=messages, user_continue_message='Continue, please.') Defensive patterns
Strategy: validation
Validate before calling
def starts_with_user(messages: list) -> bool:
return bool(messages) and messages[0].get('role') == 'user'
# or pass a default so LiteLLM handles it:
# litellm.completion(..., user_continue_message='Continue, please.') Type guard
import litellm
def is_bad_request(e: BaseException) -> bool:
return isinstance(e, litellm.BadRequestError) and 'must start with a user message' in str(e) Try / catch
try:
resp = litellm.completion(model='bedrock/...', messages=messages)
except litellm.BadRequestError as e:
if 'must start with a user message' in str(e):
messages = [{'role': 'user', 'content': 'Continue, please.'}] + messages
resp = litellm.completion(model='bedrock/...', messages=messages)
else:
raise Prevention
- When trimming history, always keep or synthesize a leading user message
- Pass user_continue_message= for Bedrock Converse calls by default
- Validate messages[0]['role'] == 'user' before every bedrock call
- Test resume-from-checkpoint flows where histories get truncated mid-conversation
When it happens
Trigger: messages=[{'role':'assistant',...}, ...] as the first element; history trimming that removes so many early turns the first surviving message is an assistant/tool turn; assistant-prefilled continuations that OpenAI tolerated but Bedrock Converse rejects as the opening turn.
Common situations: Chat apps that persist and reload trimmed histories; agent loops resuming from the model's previous answer; porting OpenAI-style conversations where assistant-first arrays were accepted.
Related errors
- BedrockException - {error_str} . Enable 'litellm.modify_para
- Setting user/encoding format is not supported by {custom_llm
- Model needs to be set for bedrock
- BedrockException: Context Window Error - {error_str}
- BedrockException - {error_str}
AI-assisted analysis of BerriAI/litellm@77b7c6c40c (2026-08-18).
Data as JSON: /api/errors/2bf944a31cd9fec4.
Report an issue: GitHub.