Bug: DeepSeek tool calling error in PentestAgent
Description
When using PentestAgent with DeepSeek through LiteLLM, tool execution sometimes fails with the following error:
LLM Error: litellm.BadRequestError: DeepseekException - {"error":{"message":"Messages with role 'tool' must be a response to a preceding message with 'tool_calls'","type":"invalid_request_error","param":null,"code":"invalid_request_error"}}It looks like PentestAgent may be sending a role: tool message back to DeepSeek without the required immediately preceding assistant message containing tool_calls.
According to DeepSeek's tool calling flow, the assistant response should contain tool_calls, then the client executes the tool, and then appends a role: tool message with the matching tool_call_id.
Environment
- OS: Windows
- Shell: PowerShell
- Python version: 3.10.10
- PentestAgent version: last
- LiteLLM version: last
Model configuration
My .env uses DeepSeek.
DEEPSEEK_API_KEY=<xxx>
PENTESTAGENT_MODEL=deepseek/deepseek-v4-pro
PENTESTAGENT_EMBEDDINGS=local
PENTESTAGENT_DEBUG=falseI also tried / plan to try:
PENTESTAGENT_MODEL=deepseek/deepseek-v4-flashSteps to reproduce
- Install PentestAgent on Windows.
- Configure
.envwith DeepSeek API key and model. - Start PentestAgent:
python -m pentestagent- Run an interactive or assist task that causes tool execution.
- After a terminal/tool result is produced, the LLM call fails with the DeepSeek error above.
Example tool output before the error:
terminal
{'command': 'type loot\\artifacts\\member_cp.html', 'timeout': 10}Then PentestAgent reports:
Messages with role 'tool' must be a response to a preceding message with 'tool_calls'Expected behavior
PentestAgent should preserve the correct tool calling message sequence when using DeepSeek through LiteLLM:
- Assistant message with
tool_calls - Tool execution
- Tool result message with matching
tool_call_id - Continue the model response
Actual behavior
DeepSeek rejects the request because a role: tool message appears without a valid preceding assistant message containing tool_calls.
Found while testing LLM provider integrations in agent frameworks: an automated tool flags candidates and each one is reproduced before it is reported. Reproduced on 8dde193 against a local endpoint, so this shows the request shape and no call to DeepSeek was made.
This looks like conversation summarization.
get_messages_with_summarysplits the history at a fixed index, so with parallel tool calls the kept window can start on atoolmessage whose assistanttool_callsturn went into the summary. With six turns of two parallel calls each and a large tool output, a 90k token history (threshold 61,440) went out like this:That is the shape your error complains about, and it would explain why it only shows up sometimes, on long runs. I can't tell from here that it is what happened in your session, but it's easy to check: the failing request should start with a
toolmessage right after the summary.Fix idea: move
split_pointback whilemessages[split_point]is a tool message, and drop leading tool messages in_truncate_to_fit, which has the same boundary problem.