# MESSAGE_COERCION_FAILURE
The exact error looks like:
```
ValueError: Message dict must contain 'role' and 'content' keys, got {'role': 'HumanMessage', 'random_field': 'random value'}
```
## Accepted shapes
- LangChain message objects (HumanMessage, AIMessage, SystemMessage, ToolMessage)
- OpenAI-style dicts: `{"role": "user", "content": "Hello world!"}`
- Tuples like `("human", "Hello")`
- Plain strings (become HumanMessage)
## Why it happens
- A dict with the wrong keys. The role must be a real role ("user", "assistant", "system"), not the class name.
- Messages that passed through JSON serialization and came back as strings, then got wrapped again.
- Custom code building dicts with extra or renamed fields.
## Fix
1. Log the messages right before the model call and check each one against the accepted shapes.
2. Normalize: convert dicts to the OpenAI-style shape with exactly `role` and `content`, or construct real message objects.
3. Find where the transformation happens (a custom serializer, a cache layer) and fix it there, not at the call site.