Documentation Index
Fetch the complete documentation index at: https://docs.zavu.dev/llms.txt
Use this file to discover all available pages before exploring further.
Debugging Functions
When your function isn’t behaving, the issue is usually in one of these layers:
┌─────────────────────────────────────────────────┐
│ 1. Inbound message (does it arrive?)
├─────────────────────────────────────────────────┤
│ 2. Agent dispatch (does the agent run?)
├─────────────────────────────────────────────────┤
│ 3. LLM call (does the LLM respond?)
├─────────────────────────────────────────────────┤
│ 4. Tool invocation (does the tool get called?)
├─────────────────────────────────────────────────┤
│ 5. Tool handler (does YOUR code work?)
└─────────────────────────────────────────────────┘
Walk the layers top-to-bottom. The first one that fails is the one to fix.
1. Did the inbound message arrive?
Open the dashboard → Senders → your sender → Conversations.
- No message visible: the inbound never reached Zavu. Check your sender’s
webhook (WhatsApp, Telegram) is wired up. The dashboard shows webhook
config and recent delivery attempts.
- Message visible, no reply: inbound is fine, problem is downstream.
Go to step 2.
2. Did the agent run?
zavu agents executions list --sender <senderId>
This is the most useful debug command. Three possible states:
a) “No executions.”
The inbound arrived but the agent dispatcher didn’t even try to invoke the
agent. Causes:
| Likely cause | Check |
|---|
Wrong SENDER_ID secret on the function | zavu fn secrets list — last 4 chars of the value must match the senderId you’re testing |
| Agent disabled | zavu agents get --sender <senderId> → enabled must be true |
| Channel filtered out | triggerOnChannels doesn’t include the inbound’s channel. Use ["*"] or include the specific one (e.g. "telegram") |
| Message type filtered out | triggerOnMessageTypes excludes the type (e.g. you sent an image and only "text" is allowed) |
| Empty message text | The dispatcher skips messages with no text. Inbound types like reactions or system events don’t trigger agents |
b) status: error
The agent ran but something blew up. Fetch the full detail:
zavu agents executions get <executionId> --sender <senderId>
You’ll see errorMessage and errorCode. Common error categories:
errorCode | Meaning | Fix |
|---|
balance_insufficient | Provider zavu ran out of AI gateway balance | Top up balance in dashboard, or switch to BYOK by setting provider + apiKey in defineAgent |
rate_limited | Hit the LLM provider’s rate limit | Lower concurrency or wait |
provider_error | LLM provider rejected the call | Check the message text — sometimes prompts trigger safety filters. Also verify your model ID is valid for the provider |
tool_not_found | Agent tried to call a tool that doesn’t exist | Tool name in defineTool doesn’t match what the LLM is calling. Or the deploy didn’t reconcile — run zavu deploy again |
tool_error | A tool handler threw | Check the tool’s source. The errorMessage includes the handler’s exception text |
c) status: success but no message sent back
The agent succeeded but didn’t reply. Look at responseText in the execution
detail — if it’s empty, the LLM returned nothing useful. Adjust your
systemPrompt to encourage replies, or check that the model isn’t returning
just tool calls without a final response.
3. Is the LLM working?
If executions are failing with provider_error:
zavu agents executions get <executionId> --sender <senderId> --json
Look at the full errorMessage. Typical issues:
- Bad model ID: e.g.
openai/gpt-4o-mini works on provider: "zavu" (our
gateway uses prefixed IDs), but the raw gpt-4o-mini works on
provider: "openai" directly.
- Expired API key (when using
provider: "openai" etc.): rotate the key
and update the agent’s apiKey.
- Tool schema malformed: rare, but if your
defineTool parameters JSON
Schema has a typo, the LLM’s tool-calling step can fail. Validate the
schema renders to valid JSON.
Once the agent runs successfully but you suspect the tool wasn’t invoked,
check the function logs:
Then send a message that should trigger the tool. You should see:
[ctx.log] <your console.log output>
If you see nothing in the function logs but agents executions shows
success, the LLM chose not to call the tool. Tighten the tool’s
description so the LLM understands when to call it.
Once you confirm the tool is being called, isolate it locally:
zavu fn invoke --tool view_menu --args '{"filter":"vegan"}'
This runs the handler with synthetic args and prints the result. Iterate
locally without redeploying.
For deeper debugging, add ctx.log calls inside the handler:
defineTool({
name: "lookup_order",
// ...
handler: async (args, ctx) => {
ctx.log("lookup_order called with", args)
const order = await fetchOrder(args.orderId)
ctx.log("found order", { id: order?.id, status: order?.status })
return order
},
})
Then zavu deploy and zavu fn logs --tail while triggering the tool from
a real conversation. The [ctx.log] lines appear in the live tail.
When you run zavu deploy, the platform reads your defineAgent /
defineTool declarations from a manifest probe Lambda invocation, then
reconciles the live agent + tools to match. If you see:
! manifest probe: manifest probe threw: defineAgent: senderId is required.
…the issue is your code threw at module load time. Common causes:
- Missing required secret: e.g.
process.env.SENDER_ID! evaluates to
undefined because you didn’t set the secret yet. Fix:
zavu fn secrets set SENDER_ID <value> then zavu deploy.
- Syntax error in your
index.ts: the bundler caught it earlier but
some edge cases get through. Run tsc --noEmit locally before deploying.
Local invocation cheat sheet
| Goal | Command |
|---|
| Test a single tool handler | zavu fn invoke --tool <name> --args '{...}' |
Test an event handler (defineFunction) | zavu fn invoke --event message.inbound --data '{...}' |
| Tail live production logs | zavu fn logs --tail |
| Query recent agent runs | zavu agents executions list --sender <senderId> |
| Full detail of one run | zavu agents executions get <executionId> --sender <senderId> |
| Inspect the agent config | zavu agents get --sender <senderId> |
| Inspect tools attached | zavu agents tools list --sender <senderId> |
| See what secrets are set | zavu fn secrets list |
When all else fails
- Add
ctx.log("got here", { someState }) aggressively in your handler.
zavu deploy and zavu fn logs --tail simultaneously.
- Trigger the flow from a real conversation.
- Read the logs top-to-bottom — the first absence of expected output is
the layer that’s failing.
- Open a ticket with the function ID, the
executionId that failed, and
the timestamp. Support can pull internal traces faster than guessing.