sigoden/aichat · error
Invalid messages
Error message
Invalid messages
What it means
parse_messages rejects the request when 'tool_results' were set (i.e. messages contained tool/role content) but the message sequence is not a valid tool-call continuation. The OpenAI-compatible endpoint requires tool result messages to follow a corresponding assistant tool_calls message.
Solutions
- Ensure every tool message is immediately preceded by an assistant message with tool_calls
- Include the full conversation history from the assistant tool_calls turn onward
- Match each tool message's tool_call_id to one of the assistant's tool_calls
- If sending results without a tool call, send them as user/assistant content instead
Example fix
// before: tool result without the assistant tool_calls turn
{"messages":[{"role":"tool","tool_call_id":"x","content":"42"}]}
// after: include the assistant turn first
{"messages":[{"role":"assistant","tool_calls":[{"id":"x",...}]},{"role":"tool","tool_call_id":"x","content":"42"}]} Defensive patterns
Strategy: validation
Validate before calling
function validateToolFlow(messages) {
for (let i = 0; i < messages.length; i++) {
const m = messages[i];
if (m.role === 'tool') {
const prev = messages[i - 1];
if (!prev || prev.role !== 'assistant' || !prev.tool_calls) {
throw new Error(`tool message at ${i} has no preceding assistant tool_calls`);
}
}
}
} Prevention
- Always send the complete tool-call conversation loop
- Match tool_call_id to assistant tool_calls
- Use an SDK that builds message arrays instead of hand-writing them
When it happens
Trigger: POST /v1/chat/completions with a messages array where a tool message appears without a preceding assistant message containing tool_calls, or tool results are present in a malformed conversation shape.
Common situations: Clients replaying tool outputs after truncating the assistant tool_calls message; hand-built request bodies missing the assistant turn; agent loops that drop intermediate messages.
Understand the failure class
Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.
Related errors
- Failed to parse '.tools
- Failed to parse '.messages
- The request was aborted because an infinite loop of…
- The call ' ' has invalid arguments
- Unexpected call
AI-assisted analysis of sigoden/aichat@82976d349a (2026-09-09).
Data as JSON: /api/errors/8692848dc1002860.
Report an issue: GitHub.
Appendix: source
Thrown at src/serve.rs:910
output.push(Message::new(
MessageRole::Assistant,
MessageContent::ToolCalls(MessageContentToolCalls::new(list, text)),
));
tool_results = None;
} else {
tool_results = Some((text, tool_calls, tool_values));
}
}
None => return Err(err()),
},
_ => {
return Err(err());
}
}
}
if tool_results.is_some() {
bail!("Invalid messages");
}
Ok(output)
}
fn parse_tools(tools: Option<Vec<Value>>) -> Result<Option<Vec<FunctionDeclaration>>> {
let tools = match tools {
Some(v) => v,
None => return Ok(None),
};
let mut functions = vec![];
for (i, tool) in tools.into_iter().enumerate() {
if let (Some("function"), Some(function)) = (
tool["type"].as_str(),
tool["function"]
.as_object()
.and_then(|v| serde_json::from_value(json!(v)).ok()),
) {View on GitHub (pinned to 82976d349a)