# CLI flags and error codes

> `--listen`, `--session-source`, `--strict-config`, websocket auth flags, JSON-RPC standard codes, overload `-32001`, `input_too_large`, and tracing via `RUST_LOG` / `LOG_FORMAT=json`.

- Repository: openai/codex
- GitHub: https://github.com/openai/codex
- Human docs: https://grok-wiki.com/public/docs/openai-codex-c82680b15ec1
- Complete Markdown: https://grok-wiki.com/public/docs/openai-codex-c82680b15ec1/llms-full.txt

## Source Files

- `src/main.rs`
- `src/error_code.rs`
- `src/server_request_error.rs`
- `src/app_server_tracing.rs`
- `README.md`
- `tests/suite/strict_config.rs`

---

---
title: "CLI flags and error codes"
description: "`--listen`, `--session-source`, `--strict-config`, websocket auth flags, JSON-RPC standard codes, overload `-32001`, `input_too_large`, and tracing via `RUST_LOG` / `LOG_FORMAT=json`."
---

The `codex-app-server` binary (`codex app-server`) exposes startup flags in `src/main.rs` and maps JSON-RPC failures through `src/error_code.rs`. Transport overload uses bounded queues (`CHANNEL_CAPACITY` = 128) and returns code `-32001`; turn input limits surface as `-32602` with structured `input_too_large` data. Process logs go to `stderr` via `tracing`, filtered by `RUST_LOG` and optionally formatted as JSON with `LOG_FORMAT=json`.

## Command entry

```bash
codex app-server [OPTIONS]
```

Flags are defined on `AppServerArgs` in `src/main.rs`. The binary calls `run_main_with_transport_options` in `src/lib.rs`, which loads config, installs tracing, and starts the selected transport.

## `--listen`

<ParamField body="--listen" type="URL" default="stdio://">
Transport endpoint. Parsed by `AppServerTransport::from_listen_url` in `app-server-transport`.
</ParamField>

| URL | Transport | Behavior |
|-----|-----------|----------|
| `stdio://` | Stdio (default) | Newline-delimited JSON on stdin/stdout |
| `unix://` | Unix socket | `$CODEX_HOME/app-server-control/app-server-control.sock` |
| `unix://PATH` | Unix socket | Custom absolute socket path |
| `ws://IP:PORT` | WebSocket | One JSON-RPC message per text frame; **experimental** |
| `off` | Off | No local listener |

<Warning>
`--listen off` with remote control disabled exits with `no transport configured; use --listen or enable remote control`. Use `off` for config-only validation (for example `tests/suite/strict_config.rs`).
</Warning>

Websocket listeners also serve HTTP probes on the same port:

- `GET /readyz` → `200` when accepting connections
- `GET /healthz` → `200` when no `Origin` header
- Requests with an `Origin` header → `403 Forbidden` (unless upgrade auth succeeds)

Loopback websocket without `--ws-auth` still rejects browser `Origin` headers during upgrade (`tests/suite/v2/connection_handling_websocket.rs`).

## `--session-source`

<ParamField body="--session-source" type="SOURCE" default="vscode">
Session metadata for product restrictions. Parsed by `SessionSource::from_startup_arg`.
</ParamField>

| Value | Maps to |
|-------|---------|
| `cli` | `SessionSource::Cli` |
| `vscode` | `SessionSource::VSCode` (default) |
| `exec` | `SessionSource::Exec` |
| `mcp`, `app-server`, `app_server`, `appserver` | `SessionSource::Mcp` |
| `unknown` | `SessionSource::Unknown` |
| Any other non-empty string | `SessionSource::Custom` (trimmed, lowercased) |

Empty values are rejected at parse time. Integrations that need custom product gates pass a stable lowercase label (for example `atlas` in plugin install tests).

## `--strict-config`

<ParamField body="--strict-config" type="bool" default="false">
Fail startup when `config.toml` (or managed config layers) contain unknown fields.
</ParamField>

When `false`, load errors produce a `config/warning` notification and the server falls back to defaults. When `true`, `ConfigManager::load_latest_config` errors propagate and the process exits non-zero.

Integration test expectation (`tests/suite/strict_config.rs`):

```bash
codex-app-server --strict-config --listen off
# stderr contains: unknown configuration field `foo`
```

## Websocket auth flags

Websocket auth applies only to `ws://` listeners. Flags are flattened from `AppServerWebsocketAuthArgs` (`app-server-transport/src/transport/auth.rs`).

<ParamField body="--ws-auth" type="MODE">
Required when any other `ws-*` flag is set. Values: `capability-token`, `signed-bearer-token`.
</ParamField>

### Capability token mode

Requires exactly one of:

<ParamField body="--ws-token-file" type="absolute path">
File containing the raw token (trimmed). Hashed to SHA-256 at startup.
</ParamField>

<ParamField body="--ws-token-sha256" type="64-char hex">
Precomputed SHA-256 digest of the token. Mutually exclusive with `--ws-token-file`.
</ParamField>

Clients send `Authorization: Bearer <token>`. The server compares `SHA-256(token)` to the configured digest with constant-time equality.

### Signed bearer token mode

<ParamField body="--ws-shared-secret-file" type="absolute path" required>
HS256 signing secret file. Secret must be at least 32 bytes after trim.
</ParamField>

<ParamField body="--ws-issuer" type="string">
Optional JWT `iss` claim match (trimmed).
</ParamField>

<ParamField body="--ws-audience" type="string">
Optional JWT `aud` claim match (string or array).
</ParamField>

<ParamField body="--ws-max-clock-skew-seconds" type="u64" default="30">
Clock skew for `exp` / `nbf` validation.
</ParamField>

JWT must include `exp`. `alg=none` tokens are rejected.

### Startup guards

```text
Non-loopback bind (e.g. ws://0.0.0.0:0) without --ws-auth
  → startup error: refusing to start non-loopback websocket listener ... without auth

Short shared secret (< 32 bytes)
  → startup error: must be at least 32 bytes
```

Loopback (`127.0.0.1`) may start without auth, but remote clients should still configure auth before exposing non-loopback addresses.

## Other flags

| Flag | Notes |
|------|-------|
| `--remote-control` | Hidden. Enables remote-control transport when SQLite state DB is available |
| `--disable-plugin-startup-tasks-for-tests` | Debug builds only; skips plugin startup tasks |

## JSON-RPC error codes

Standard JSON-RPC 2.0 codes are centralized in `src/error_code.rs`:

| Code | Constant | Typical use |
|------|----------|-------------|
| `-32600` | `INVALID_REQUEST_ERROR_CODE` | Malformed envelope, duplicate `initialize`, **Not initialized**, unsupported experimental method without opt-in |
| `-32601` | `METHOD_NOT_FOUND_ERROR_CODE` | Unknown or unsupported RPC (for example `thread/turns/items/list is not supported yet`) |
| `-32602` | `INVALID_PARAMS_ERROR_CODE` | Bad params, validation failures, **`input_too_large`** |
| `-32603` | `INTERNAL_ERROR_CODE` | Unexpected failures (turn start, thread load, realtime, and similar) |
| `-32001` | `OVERLOADED_ERROR_CODE` | Ingress queue saturated (retryable) |

Exported for embedders: `INVALID_PARAMS_ERROR_CODE`, `INPUT_TOO_LARGE_ERROR_CODE` (`"input_too_large"`).

### Overload (`-32001`)

```mermaid
sequenceDiagram
    participant Client
    participant Transport as transport ingress
    participant Queue as CHANNEL_CAPACITY=128
    participant Processor

    Client->>Transport: JSON-RPC request
    Transport->>Queue: try_send IncomingMessage
    alt queue has capacity
        Queue->>Processor: dispatch
        Processor-->>Client: result / notifications
    else queue full (request only)
        Transport-->>Client: error -32001 "Server overloaded; retry later."
    end
```

- Socket transports: `app-server-transport` `enqueue_incoming_message` returns the overload error on the connection writer when `transport_event_tx` is full.
- In-process API: same code with messages `in-process app-server request queue is full` or `in-process server request queue is full` (`src/in_process.rs`).

<Note>
Treat `-32001` as retryable. Use exponential backoff with jitter. Notifications and responses may block instead of failing when the queue is full.
</Note>

### `input_too_large`

`turn/start` and related paths call `validate_v2_input_limit` in `src/request_processors/turn_processor.rs`. Limit: `MAX_USER_INPUT_TEXT_CHARS` = `1 << 20` (1,048,576) summed across `V2UserInput` text.

<RequestExample>

```json
{
  "jsonrpc": "2.0",
  "id": 3,
  "error": {
    "code": -32602,
    "message": "Input exceeds the maximum length of 1048576 characters.",
    "data": {
      "input_error_code": "input_too_large",
      "max_chars": 1048576,
      "actual_chars": 1048577
    }
  }
}
```

</RequestExample>

Clients should branch on `error.data.input_error_code == "input_too_large"` rather than parsing the message string.

### Common `-32600` messages

| Message | When |
|---------|------|
| `Not initialized` | RPC before `initialize` / `initialized` on that connection |
| `Already initialized` | Second `initialize` on same connection |
| `Invalid request: …` | JSON-RPC envelope cannot deserialize to `ClientRequest` |

### Server-request errors

`src/server_request_error.rs` documents `turnTransition` in `error.data.reason` when a pending server request is resolved because turn state changed. This is not a fixed JSON-RPC code; detect via `data.reason == "turnTransition"`.

## Tracing and logs

Tracing is installed in `run_main_with_transport_options` before transports start:

| Variable | Effect |
|----------|--------|
| `RUST_LOG` | `EnvFilter::from_default_env()` — standard `tracing-subscriber` directives (for example `codex_app_server=debug`, `warn`) |
| `LOG_FORMAT=json` | `stderr` emits one JSON object per log line with full span events |

Default format is human-readable text to `stderr`. OpenTelemetry layers may attach when configured (`OTEL_SERVICE_NAME` = `codex-app-server`).

Per-request spans (`src/app_server_tracing.rs`) include:

- `rpc.system` = `jsonrpc`
- `rpc.method`, `rpc.transport` (`stdio`, `unix_socket`, `websocket`, `in-process`)
- `rpc.request_id`, `app_server.connection_id`
- `app_server.client_name` / `app_server.client_version` after `initialize`

Inbound JSON-RPC may carry W3C `trace` fields; invalid carriers are logged and ignored.

<Steps>

<Step title="Enable debug logs for a local run">

```bash
RUST_LOG=codex_app_server=debug,info codex app-server --listen stdio://
```

</Step>

<Step title="Emit JSON logs for log aggregators">

```bash
RUST_LOG=info LOG_FORMAT=json codex app-server --listen ws://127.0.0.1:8765 \
  --ws-auth capability-token --ws-token-file /absolute/path/to/token
```

</Step>

</Steps>

## Quick reference

```text
codex-app-server
  --listen stdio:// | unix://[PATH] | ws://HOST:PORT | off
  --session-source vscode | cli | exec | app-server | <custom>
  --strict-config
  --ws-auth capability-token | signed-bearer-token
    + token file OR sha256 digest
    OR shared-secret file + optional issuer/audience/skew

JSON-RPC errors: -32600 .. -32603, -32001 overload
input_too_large: -32602 + data.input_error_code

Logs: RUST_LOG, LOG_FORMAT=json → stderr
```

## Related pages

<CardGroup>
<Card title="Installation" href="/installation">
Default listen URLs, session source, and logging env vars at launch.
</Card>
<Card title="Protocol and transport" href="/protocol-and-transport">
JSON-RPC wire rules, queue backpressure, and transport comparison.
</Card>
<Card title="Transports and proxy" href="/transports-and-proxy">
Choosing `--listen`, control socket paths, proxy bridging, and websocket pairing.
</Card>
<Card title="Troubleshooting" href="/troubleshooting">
`Not initialized`, overload retries, strict config failures, and Origin rejections.
</Card>
</CardGroup>
