# Logs and debugging

> Read Edge Function logs on SupaHost in the Functions tab, over MCP or SSH, understand router error codes like BOOT_ERROR and IDLE_TIMEOUT, and debug failing functions.

Source: https://supahost.app/docs/functions/logs · Updated 2026-11-11

Each project with functions keeps one log of everything its functions print and every request they answer. You can read it in the dashboard, from an AI agent over MCP, or over SSH on Business plans. There's no searchable log explorer, and the project **Logs** tab is still marked coming soon, so this function log is where to look.

## Where to read logs

### The Logs card

*Dashboard → your project → **Functions** tab → **Logs** card*. It shows the newest 200 entries with Time, Function (with the version that ran), Level and Message. Pick a function from the **All functions** menu to filter, or click **Refresh**. The card also refreshes itself every 10 seconds, so you can leave it open while you test.

Every member of the team can read the Logs card.

### From an AI agent

The MCP tool `get_edge_function_logs` returns recent entries, newest first. It takes an optional `function_slug` and a `limit` of up to 500 (default 100). A prompt like "Read the last 50 log lines for `invoice-reminders` and tell me why it returns 500" works well. See [MCP tools reference](/docs/ai-tools/mcp-tools).

### Over SSH (Business and Enterprise)

With [SSH access](/docs/platform/ssh) set up, print the last 200 lines, or follow new ones:

```bash
ssh supahost@acme.supahost.space
# then, on the instance:
sudo supahost-logs app-one functions            # last 200 lines
sudo supahost-logs app-one functions --follow   # keep streaming
```

## What gets logged

| Type | Level | What it contains |
|---|---|---|
| `log` | info, warn or error | Your `console.log` / `console.info`, `console.warn`, `console.error` output |
| `request` | info, or error for 5xx | One line per request: method, path, status and duration, e.g. `POST /invoice-reminders → 200 in 182 ms` |
| `boot` | info or error | "Booted in N ms", or the reason a worker failed to start |
| `exception` | error | Uncaught exceptions with the stack trace |
| `shutdown` | info | A worker stopping, with CPU time and memory used |

Log structured values to make them easier to read later:

```ts
console.log(JSON.stringify({ event: "reminder_sent", invoiceId: invoice.id, ms: Date.now() - t0 }));
console.error("Resend rejected the email", res.status, await res.text());
```

Don't log secrets, access tokens or whole request headers.

## How long logs are kept

Retention is by size, not time. The log file is capped at 8 MB; when it passes that, it's trimmed to the most recent 2 MB. Readers look at the last 3 MB and return at most 1000 entries. A quiet project keeps days of history; a busy one may keep only minutes.

If you need a lasting record (payments, audit events), write it to a table from the function instead of relying on the log.

## Error codes from the router

When something fails outside your code, the router answers with a JSON body and an `sb-error-code` header:

| Status | `sb-error-code` | What it means | Where to look |
|---|---|---|---|
| 404 | `NOT_FOUND` | No live function with that name. | Check the name and that the status is **Live**. [Returns 404](/kb/function-404) |
| 401 | `UNAUTHORIZED_NO_AUTH_HEADER` | **Require a JWT** is on and no `Authorization` header was sent. | [Returns 401](/kb/function-401) |
| 401 | `UNAUTHORIZED_INVALID_JWT` | The token isn't a valid, unexpired JWT from this project. | [Returns 401](/kb/function-401) |
| 503 | `BOOT_ERROR` | The worker couldn't start, often an error thrown at the top level of the module or a missing secret read at boot. | `boot` lines in the logs. [503 or 504](/kb/function-503-504) |
| 546 | `WORKER_RESOURCE_LIMIT` | The worker ran out of resources (for example the 150 MB memory limit). The router retries such requests up to 2 times first. | `shutdown` lines show memory used. [Memory limit](/kb/function-memory-limit) |
| 504 | `IDLE_TIMEOUT` | The function didn't start responding within 150 seconds. | [Timeouts](/kb/function-timeout) |
| 500 | `WORKER_ERROR` | The function threw an error that wasn't caught. | `exception` lines. [Returns 500](/kb/function-500) |

Responses your own code returns never have `sb-error-code`, so that header tells you which side produced the error.

## A debugging routine

1. **Reproduce it in the dashboard.** The **Test a function** card (owner only) sends a real request as the anon key or as `service_role` and shows status, duration, any `sb-error-code` and up to 64 KB of the body.
2. **Read the logs for that request.** Filter the Logs card to the function. Look for `exception` or `boot` errors just before the `request` line.
3. **Add logging around the suspect code**, deploy a new version, and repeat. Each deploy takes about a minute, and the old version keeps serving until the new one is live.
4. **Check the deploy status.** If the row says **Deploy failed**, your latest code isn't running at all; the build error is under the function's URL.
5. **Check the caller.** A `401` with an `sb-error-code` means the request never reached your code. Compare the token you're sending with [Authentication and JWT verification](/docs/functions/auth).
6. **Run it locally** with `supabase functions serve` if you need a debugger or faster iteration (see [Local development with the Supabase CLI](/docs/local-development/overview)).

## Related

- [Edge Function limits](/docs/functions/limits)
- [Edge Function deploy failed](/kb/function-deploy-failed)
- [Monitoring, health checks and alerts](/docs/platform/monitoring)
