# Error handling

> How Shoplazza CLI reports failures through JSON success and error envelopes and exit codes, and how scripts or AI agents should react to each of them.

How Shoplazza CLI reports failures, and how to handle them in scripts and agents.

## Success and error envelopes

On success, data goes to **stdout** as a JSON envelope:

```json
{ "ok": true, "action": "products.list", "data": { } }
```

On failure, an error envelope goes to **stderr**:

```json
{ "error": { "type": "api", "message": "Product not found", "hint": "Check the product ID and try again" }, "request_id": "req_abc123" }
```

Always read the `hint` — it tells you the next action. Keep the `request_id` for support. See [Error format](../reference/error-format) for the full structure.

## Exit codes

Branch on the exit code rather than parsing text:

| Code | Type | Typical fix |
|------|------|-------------|
| `0` | Success | — |
| `1` | API error | Inspect the response body / `request_id` |
| `2` | Validation error | Fix flags or input JSON |
| `3` | Authentication error | `shoplazza auth login` |
| `4` | Network error | Check connectivity, retry |
| `5` | Internal error | Report a bug |

```shell
shoplazza products list
case $? in
  0) echo "ok" ;;
  3) shoplazza auth login --store-domain my-store.myshoplaza.com --domain products ;;
  4) echo "network — retrying"; sleep 2; shoplazza products list ;;
  *) echo "failed (code $?)" ;;
esac
```

See the full table in [Exit codes](../reference/exit-codes).

## Preview before writing

For any write, preview the request with `--dry-run` before executing:

```shell
shoplazza discounts +percent-code --target order --percent 20 --code SAVE20 --dry-run
```

## Retries

Retry only transient failures (network, `4`; some `1` rate-limit responses). Do not blindly retry validation (`2`) or auth (`3`) errors — fix the cause first. In CI, cap retries and back off.

## See also

* [Exit codes](../reference/exit-codes)
* [Error format](../reference/error-format)
* [Output formats](./output-formats)
