# Debugging

Before reaching for logs, open the [inspector](/agentos/docs/inspector): it shows the live transcript, pending permission requests (the most common reason an agent looks stuck), and the process tree.

Two log streams help diagnose what's happening inside a VM: the **agent's** own output and the **runtime (sidecar)** logs.

## Agent logs (`onAgentStderr`)

The coding agent (ACP adapter) runs as a process inside the VM and uses **stdout for the ACP protocol**, so its **stderr** carries the agent's logs, warnings, and crash output — the first place to look when a tool call or session fails mid-turn. Capture it with `onAgentStderr` on the VM:

examples/debugging/agent-logs.ts:

```ts
// Capture the coding agent's stderr at the VM level to diagnose tool calls,
// model errors, and crashes mid-turn.

import { AgentOs } from "@rivet-dev/agentos";
import pi from "@agentos-software/pi";

const agentOs = await AgentOs.create({
  software: [pi],
  onAgentStderr(event) {
    // event: { sessionId, agentType, processId, pid, chunk: Uint8Array }
    process.stderr.write(`[agent:${event.agentType}] `);
    process.stderr.write(event.chunk);
  },
});

await agentOs.dispose();
```

It's a VM-level option covering every session's agent process; if omitted, chunks are written to the host `process.stderr` by default. See [Sessions → Agent logs](/agentos/docs/sessions#agent-logs).

## Agent crashes (`onAgentExit`)

If an adapter exits unexpectedly, the runtime logs the exit, does **not** restart it or replay an uncertain prompt, and fires `onAgentExit`:

```ts
const agentOs = await AgentOs.create({
  software: [pi],
  onAgentExit(event) {
    // restart is always "not_attempted". A later explicit prompt restores the
    // durable session through resume, load, or bounded transcript continuation.
    console.warn(`agent exited (code ${event.exitCode}), restart=${event.restart}`);
  },
});
```

The durable session and committed SQLite history remain available. The crash *reason* is on the adapter's stderr; the exit event reports that the live runtime disappeared. A later explicit prompt performs restoration. See [Sessions](/agentos/docs/sessions).

## Runtime logs (sidecar)

The agentOS sidecar emits structured **logfmt** logs for request handling, networking, and lifecycle. Configure them with environment variables on the **host process** (the sidecar inherits the host environment):

| env var | effect |
|---------|--------|
| `AGENTOS_LOG_LEVEL` / `LOG_LEVEL` / `RUST_LOG` | log filter, in that priority. Uses [EnvFilter](https://docs.rs/tracing-subscriber/latest/tracing_subscriber/filter/struct.EnvFilter.html) syntax, e.g. `debug`, `info`, `agentos_sidecar=debug,info`. Default `info`. |
| `RUST_LOG_FORMAT` | `logfmt` (default) or `text` |
| `AGENTOS_LOG_FILE` | append logs to this file instead of stderr (never stdout, which carries the wire protocol) |
| `RUST_LOG_{SPAN_NAME,SPAN_PATH,TARGET,LOCATION,MODULE_PATH,ANSI_COLOR}` | per-field toggles (`=1` to enable) |

```bash
AGENTOS_LOG_LEVEL=debug AGENTOS_LOG_FILE=./sidecar.log RUST_LOG_FORMAT=logfmt node app.mjs
```

Produces logfmt lines such as:

```text
ts=2026-… level=info  message="ext request received" kind=create_session
ts=2026-… level=info  message="ext request handled"  kind=create_session elapsed_ms=1798
ts=2026-… level=debug message="querying: api.anthropic.com. A"
```

> **NOTE:** Most sidecar log activity is on the session/ACP path. A bare `AgentOs.create()` or a single `exec()` emits almost nothing — create a session (and send a prompt) to see request-handling logs.

Use **agent logs** to see what the agent did (tool calls, model errors), and **runtime logs** to see what the sidecar did around it (request timing, DNS, lifecycle).
