> ## 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

> Diagnose why your agent isn't responding, tool calls are failing, or deploys are stuck — in order from most-likely cause to least.

## 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.

<Tip>
  If you use Claude Code / Cursor / Copilot, install [Zavu's Coding Agent Skills](/tools/coding-agent-skills)
  and ask your assistant "why isn't my agent responding?" — it'll run the
  diagnostic commands below in order and interpret the output.

  ```sh theme={null}
  npx skills add zavudev/zavu-skills
  ```
</Tip>

## 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?

```sh theme={null}
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:

```sh theme={null}
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`:

```sh theme={null}
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.

## 4. Did the tool get called?

Once the agent runs successfully but you suspect the tool wasn't invoked,
check the **function logs**:

```sh theme={null}
zavu fn logs --tail
```

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.

## 5. Is the tool handler buggy?

Once you confirm the tool is being called, isolate it locally:

```sh theme={null}
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:

```ts theme={null}
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.

## Reconcile didn't pick up my new agent / tool

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

1. Add `ctx.log("got here", { someState })` aggressively in your handler.
2. `zavu deploy` and `zavu fn logs --tail` simultaneously.
3. Trigger the flow from a real conversation.
4. Read the logs top-to-bottom — the first absence of expected output is
   the layer that's failing.
5. Open a ticket with the function ID, the `executionId` that failed, and
   the timestamp. Support can pull internal traces faster than guessing.
