How to Debug LLM Tool Calls
How to Debug LLM Tool Calls
LLM tool calls fail in ways that look random until you inspect the full execution path. The model may choose the wrong tool, pass malformed arguments, call tools in the wrong order, skip a required call, retry forever, or use stale context. In production, these failures usually come from unclear tool definitions, weak routing instructions, missing state, brittle schemas, or poor observability.
If your team is building agents, AI workflows, or LLM features that call APIs, databases, search indexes, calculators, ticketing systems, or internal services, you need a debugging process that goes beyond reading the final answer. You need to inspect each model decision, tool input, tool output, latency, retry, and prompt version.
Start by Capturing the Full Tool Call Trace
You cannot debug tool calls from the final response alone. A user may see “I couldn’t find that order,” but the real problem could be one of several issues:
- The model never called the order lookup tool.
- The model called the tool with the wrong customer ID.
- The tool returned a 500 error.
- The tool returned valid data, but the model ignored it.
- The model called the correct tool before it had enough information.
For each request, capture at least these fields:
- User input: the exact message or event that triggered the workflow.
- System and developer instructions: including prompt version and runtime variables.
- Available tools: names, descriptions, JSON schemas, and required fields.
- Model choice: tool selected, arguments generated, or decision to answer directly.
- Tool execution result: response body, status code, error, timeout, and latency.
- Intermediate messages: tool results returned to the model and follow-up reasoning context where available.
- Final answer: what the user or downstream system received.
This is where LLM observability becomes essential. Standard application logs tell you whether an API failed. LLM traces tell you why the model chose a specific tool, what arguments it generated, and how the result affected the next step.
Classify the Failure Before You Fix It
Do not start by changing the prompt. First, classify the failure. Most LLM tool call bugs fall into one of these categories.
1. Tool Selection Failure
The model picked the wrong tool or answered without calling a required tool.
Example: A support agent should call get_refund_policy before answering refund questions. Instead, it answers from general knowledge and gives a policy that expired 6 months ago.
Common causes:
- Tool descriptions overlap.
- The prompt does not say when the tool is mandatory.
- The model has enough prior knowledge to guess, even though guessing is unsafe.
- The user intent is ambiguous and no clarification step exists.
2. Argument Generation Failure
The model selected the correct tool but passed bad arguments.
Example: The model calls search_orders with {"email": "john"} instead of using the full email address available in the session.
Common causes:
- Schema fields are vague, such as
idinstead ofcustomer_id. - The prompt does not define where values should come from.
- Runtime context contains multiple similar identifiers.
- The schema allows weak or partial values.
3. Tool Execution Failure
The model made a valid call, but the tool failed.
Example: The agent calls create_jira_ticket, but the API returns a 401 because the integration token expired.
Common causes:
- Authentication or permissions changed.
- The upstream service is down or rate limited.
- The tool has a bug outside the LLM layer.
- The model passed a valid value that your backend did not handle.
4. Tool Result Interpretation Failure
The tool returned useful data, but the model misread it, ignored it, or overgeneralized from it.
Example: A billing tool returns {"plan": "trial", "expires_at": "2026-02-01"}. The model tells the user they are on a paid plan because it sees an active expiration date.
Common causes:
- The tool response is too raw or overloaded.
- The model has no instruction for interpreting edge cases.
- The response includes fields with similar names.
- The prompt asks for a concise answer but not factual grounding.
5. Sequencing Failure
The model calls tools in the wrong order or stops too early.
Example: A travel agent calls book_flight before calling confirm_user_preferences.
Common causes:
- The workflow has hidden business rules that are not in the prompt.
- Tool descriptions do not state prerequisites.
- The model is expected to infer a multi-step process from examples alone.
- There is no state machine or guardrail around destructive actions.
Inspect the Tool Definition First
Many tool call bugs come from weak tool definitions. The model only sees the name, description, and schema you provide. If those are vague, the model will make reasonable but wrong guesses.
A weak tool definition looks like this:
{
"name": "lookup",
"description": "Looks up user data",
"parameters": {
"type": "object",
"properties": {
"id": { "type": "string" }
}
}
}This definition leaves too much open. What kind of user data? Which ID? When should the tool be used? What should the model do if the ID is missing?
A stronger version is more specific:
{
"name": "get_customer_subscription",
"description": "Use this tool to retrieve a customer's current subscription plan, renewal date, trial status, and cancellation status. Call this before answering any question about billing, plan access, renewal, cancellation, or trial expiration.",
"parameters": {
"type": "object",
"required": ["customer_id"],
"properties": {
"customer_id": {
"type": "string",
"description": "The internal customer ID from session context. Do not use email, name, or account slug."
}
}
}
}Good tool definitions do four things:
- Name the business object clearly: use
get_customer_subscription, notlookup. - State when to use the tool: include the user intents that require it.
- State when not to use it: this matters when tools overlap.
- Define argument sources: tell the model whether to use session context, user input, retrieved documents, or a previous tool result.
Make Tool Schemas Hard to Misuse
Schema design is one of the best ways to reduce tool call errors. Treat tool schemas as part of your model interface, not as a thin wrapper over your internal API.
Use these patterns:
- Prefer specific fields: use
customer_id,order_id, andticket_idinstead ofid. - Use enums where possible: if priority can only be
low,medium, orhigh, define it that way. - Require fields that your backend truly needs: optional fields invite incomplete calls.
- Add format descriptions: for example, “ISO 8601 date in YYYY-MM-DD format.”
- Split overloaded tools: one generic
update_recordtool is harder to control thanupdate_shipping_addressandcancel_subscription.
Also avoid exposing low-level internal APIs directly to the model. Create model-facing tools that match the task. If your internal refund API needs 12 fields, but the model only knows 4 of them, write a wrapper that fills defaults, validates state, and returns a compact result.
Compare Expected Calls Against Actual Calls
For each failed trace, write down the tool behavior you expected before changing anything.
Use a simple table during debugging:
- User input: “Can I still cancel my plan before renewal?”
- Expected tool call:
get_customer_subscription(customer_id) - Actual tool call: none
- Expected final answer: answer based on renewal date and cancellation status
- Actual final answer: generic cancellation policy
- Likely failure class: tool selection failure
This keeps your team from mixing several bugs together. If the model skipped the tool, fix tool selection first. Do not start tuning the final response style.
Use Minimal Repro Cases
Production traces are noisy. They include long chat history, retrieval results, user metadata, feature flags, and application state. To debug tool behavior, reduce the case until the failure still happens with the smallest possible input.
Start with the failing trace, then remove one variable at a time:
- Keep the same model, prompt version, and tool definitions.
- Replace long chat history with the last 1 to 3 relevant turns.
- Remove unrelated retrieved documents.
- Keep only the context fields needed for the tool call.
- Run the same input 10 to 20 times if the failure is intermittent.
If the failure disappears, add the removed context back until it returns. This often reveals collisions, such as a retrieved document telling the model to answer from policy text while the system prompt requires a billing tool call.
Check Whether the Model Has Enough State
Many tool call errors are state errors. The model cannot call the right tool if it lacks the required identifier, permission status, or workflow stage.
For every tool call, ask:
- Does the model know which user, account, workspace, or organization this request belongs to?
- Does it know whether the user is allowed to perform the action?
- Does it know whether this is a draft, preview, or committed action?
- Does it know whether a previous tool call already completed the task?
- Does it know what to do if a required value is missing?
A common bug happens when the app has state but the model does not. For example, your backend knows the active workspace_id, but the tool schema asks the model to provide it. If the model cannot see that ID, it may invent one, reuse an old one, or ask the user for information your app already has.
Fix this by injecting the right state into the prompt, binding it server-side, or removing it from the model-generated arguments. For sensitive fields, prefer server-side binding. The model should not choose a tenant ID, permission level, or payment account if your application already knows the correct value.
Separate Model Bugs From Tool Bugs
When tool calling fails, teams often blame the model too early. Verify the tool itself with deterministic tests.
For each tool, create test cases for:
- Valid input with a normal response.
- Missing required input.
- Invalid IDs.
- Permission-denied responses.
- Timeouts and retries.
- Empty result sets.
- Large result sets.
Then test the LLM layer separately. Give the model a fixed prompt, fixed context, and fixed tool definitions. Check whether it selects the right tool and produces valid arguments. This split helps you avoid prompt changes that hide a backend issue.
Add Guardrails Around High-Risk Tools
Some tools should not be controlled only by model behavior. If a tool sends money, deletes data, changes permissions, books travel, sends external messages, or updates production systems, add deterministic checks.
Use guardrails such as:
- Confirmation steps: require explicit user confirmation before destructive actions.
- Permission checks: verify access server-side before executing the tool.
- Argument validation: reject malformed, missing, or unsafe values before calling the API.
- Dry runs: show what would happen before committing the action.
- Idempotency keys: prevent duplicate charges, tickets, or bookings during retries.
- Allowlists: restrict which records, domains, or actions the model can touch.
For example, a tool named send_customer_email should usually support a preview mode. The model can draft the email and call preview_customer_email, while the actual send step requires a separate confirmation path.
Evaluate Tool Calls, Not Only Final Answers
A final answer can look correct even when the tool path is unsafe. An agent may answer a billing question correctly once from memory, then fail next week when the policy changes. Your evals should check whether the model used the right tool, with the right arguments, in the right order.
A good LLM evaluation for tool calls should test:
- Tool selection: did the model call the required tool?
- Argument accuracy: did it pass the correct IDs, dates, filters, and enum values?
- Call order: did it gather required information before acting?
- Result grounding: did the final answer match the tool output?
- Refusal behavior: did it avoid calling tools when the request was unauthorized or impossible?
- Recovery behavior: did it handle tool errors, empty results, and timeouts correctly?
For more complex workflows, you can use an LLM-as-a-judge setup to grade traces. The judge should inspect structured events, not only natural-language answers. For example, ask it to compare expected and actual tool calls, then return a score with a failure category.
Build a Tool Call Regression Dataset
Every production tool call bug should become a regression test. Store the failing input, prompt version, tool definitions, required state, expected tool calls, and expected final behavior.
Include cases such as:
- Ambiguous user requests that require a clarification question.
- Requests with missing identifiers.
- Users who lack permission for an action.
- Tool responses with empty results.
- Tool responses with multiple matching records.
- Requests that require 2 to 5 tool calls in sequence.
- Requests where the model must not call a tool.
A useful starting dataset might have 50 to 100 examples for one workflow. For a high-traffic or high-risk agent, you may need hundreds or thousands. The goal is not full coverage on day one. The goal is to make sure known failures do not return after a prompt edit, model upgrade, schema change, or new tool rollout.
Debug Multi-Step Tool Chains Carefully
Multi-step agents add another layer of failure. The first call may succeed, but its output may cause the next call to fail. You need to inspect the chain as a sequence, not as isolated calls.
For a workflow like “find the customer, check subscription, apply discount, send confirmation,” track each step:
search_customerreturns one exact match.get_subscriptionreturns an active annual plan.apply_discountreturns success with discount ID.send_confirmationsends the correct message to the correct email.
If step 3 fails, the model should not proceed to step 4 as if the discount was applied. Your prompt and orchestration code should define what happens after each class of tool result.
If you are compiling or orchestrating more structured LLM workflows, an LLM compiler pattern can help turn higher-level tasks into planned execution steps. Even then, you still need traceability and tests for each tool boundary.
Watch for Common Prompt Problems
Prompt wording can cause tool failures even when schemas are clean. Look for these issues:
- Conflicting instructions: one instruction says “answer quickly,” while another says “always verify account state first.”
- Soft requirements: “You may use the billing tool” is weaker than “Call the billing tool before answering billing questions.”
- Too many tools at once: exposing 30 tools increases routing mistakes, especially when names overlap.
- Missing error policy: the model does not know what to say when a tool times out.
- Examples that teach shortcuts: few-shot examples answer directly even though production behavior requires tools.
For mandatory tool use, be explicit:
Before answering any question about subscription status, renewal date, cancellation, invoices, or trial expiration, call get_customer_subscription using the customer_id from session context. If customer_id is missing, ask the user to sign in. Do not answer these questions from general knowledge.This kind of instruction is boring, but it works better than broad guidance like “Use tools when helpful.”
Handle Tool Errors as First-Class Cases
Many agents work in happy-path demos and fail in production because tool errors are undefined. Decide how the model should respond to each error class.
Define behavior for:
- Timeout: retry once, then tell the user the system could not complete the lookup.
- Rate limit: do not retry aggressively. Ask the user to try again later or queue the task.
- Permission denied: explain that the user does not have access. Do not expose internal details.
- Not found: ask for another identifier or state that no matching record was found.
- Multiple matches: ask a clarifying question before taking action.
- Validation error: correct the argument if possible, otherwise ask for the missing field.
Return structured tool errors when possible:
{
"ok": false,
"error_code": "MULTIPLE_MATCHES",
"message": "Three customers matched this email domain.",
"next_action": "ASK_USER_TO_SELECT_CUSTOMER"
}This gives the model a clear path. Raw stack traces or generic 500 messages give it very little to work with.
Use Deterministic Controls Where the Model Should Not Decide
Do not ask the model to decide everything. Some parts of tool execution should live in application code.
Good candidates for deterministic code include:
- Tenant and workspace selection.
- Permission checks.
- Payment limits.
- Data deletion rules.
- PII redaction.
- Retry limits.
- Idempotency handling.
- Tool allowlists by user role.
For example, if a user says, “Delete my workspace,” the model can classify the intent and draft a confirmation. Your backend should verify ownership, require confirmation, enforce waiting periods if needed, and execute the deletion. The model should not be the only control point.
Measure Tool Call Quality in Production
Track metrics that reveal tool behavior, not only user satisfaction or final response ratings.
Useful metrics include:
- Tool call rate by intent: percentage of billing questions that call the billing tool.
- Invalid argument rate: percentage of calls rejected by schema or backend validation.
- Tool error rate: grouped by timeout, auth failure, rate limit, and validation error.
- Retry rate: repeated calls to the same tool in one request.
- Unnecessary call rate: tool calls made when the user only needed a general answer.
- Grounding failure rate: final answers that conflict with tool output.
- Latency by tool: especially p95 and p99 latency for user-facing workflows.
These metrics help you decide where to invest. If invalid arguments are high, improve schemas and context. If the model skips required tools, improve routing instructions and evals. If tool latency dominates, optimize backend services or move long-running work into async flows.
A Practical Debugging Checklist
When an LLM tool call fails, work through this checklist:
- Open the full trace, including prompt, tool definitions, model calls, tool results, and final response.
- Classify the failure as selection, arguments, execution, interpretation, sequencing, or state.
- Write the expected tool call and expected final behavior.
- Check whether the model had the required state and identifiers.
- Review tool names, descriptions, required fields, enums, and field descriptions.
- Run a minimal repro case with the same model and prompt version.
- Test the tool directly outside the LLM flow.
- Add or update deterministic validation for risky actions.
- Add the case to your regression dataset.
- Run evals before shipping the fix.
What Good Debugging Looks Like
Good LLM tool debugging is systematic. You do not guess based on the final answer. You inspect the trace, classify the failure, tighten the interface, add the missing state, and protect the workflow with tests.
The best fixes are often small: rename a tool, add a required field, bind an ID server-side, split one overloaded tool into two specific tools, or add one eval case that catches a recurring bug. Over time, these changes make your LLM application more predictable and easier to ship.
PromptLayer helps AI teams trace tool calls, manage prompt versions, build regression datasets, and evaluate LLM workflows before they reach production. If you are debugging agents or tool-heavy LLM applications, create a PromptLayer account and start tracking the full path from prompt to tool call to final response.