# Error Reports CLI Tool `scripts/error-reports.mjs` is a Node.js CLI tool for querying Penpot error reports via the RPC API. Provides access to error logs with filtering, pagination, and multiple output formats. ## When to use - Querying error reports from the database for debugging or analysis - Filtering errors by source, kind, tenant, or backend version - Exporting error data in JSON, NDJSON, or table format - Computing error statistics (top signatures, version, source, audit-log kind, hourly distribution, bursts, heatmap) - Investigating specific error reports by ID ## Prerequisites - Node.js with `commander` and `dotenv` packages installed (in root `package.json`) - Running Penpot backend with error-reports RPC endpoints - Access token with `error-reports:read` permission ## Configuration Create a `.env` file in the project root: ```bash PENPOT_API_URI=http://localhost:3450 PENPOT_ACCESS_TOKEN= ``` Grant the required permission to your access token: ```sql UPDATE access_token SET perms = ARRAY['error-reports:read']::text[], updated_at = now() WHERE id = ''; ``` ## Usage ```bash ./scripts/error-reports.mjs [options] ``` ### Commands #### `list` - List error reports with pagination and filters ```bash ./scripts/error-reports.mjs list [options] ``` **Options:** | Flag | Description | Default | |------|-------------|---------| | `-l, --limit ` | Max items per page (max: 200) | `50` | | `--from ` | ISO timestamp — oldest boundary (items after this) | — | | `--to ` | ISO timestamp — newest boundary (items before this) | — | | `--since ` | ISO timestamp — explicit cursor for manual pagination | — | | `--since-id ` | Fetch errors after this ID (cursor pagination) | — | | `-s, --source ` | Filter by source (see source names below) | — | | `-p, --profile-id ` | Filter by profile ID | — | | `-k, --kind ` | Filter by kind (string) | — | | `-t, --tenant ` | Filter by tenant (string) | — | | `--version ` | Filter by version | — | | `--hint ` | Filter by hint (ILIKE match) | — | | `-a, --all` | Fetch all pages automatically (streams output) | `false` | | `-f, --format ` | Output format: `json`, `table`, or `ndjson` | `table` | | `--normalize-hints` | Normalize hints by stripping dynamic values | `false` | | `-o, --output ` | Write output to file instead of stdout | — | | `--env ` | Custom .env file path | `.env` | | `-h, --help` | Show help message | — | **Streaming behavior:** With `--all`, output must be `ndjson` or `table`; `--all --format json` is rejected because `--all` streams output. `--all --format table` prints rows immediately. `--format ndjson` always streams one JSON object per line. #### `get` - Get a single error report by ID ```bash ./scripts/error-reports.mjs get [options] ``` **Options:** | Flag | Description | Required | |------|-------------|----------| | `--id ` | Error report ID | Yes (or --error-id) | | `--error-id ` | Error report error-id | Yes (or --id) | | `-f, --format ` | Output format: `json` or `table` | No (default: `table`) | | `--env ` | Custom .env file path | No (default: `.env`) | | `-h, --help` | Show help message | No | #### `stats` - Compute error report statistics ```bash ./scripts/error-reports.mjs stats [options] ``` Reads from `--input `, stdin (piped), or fetches from API. Computes aggregations by signature, version, source, audit-log kind, hour, optional 5-minute bursts, and optional day-of-week × hour heatmap. **Options:** | Flag | Description | Default | |------|-------------|---------| | `--from ` | Start of interval (ISO timestamp) | — | | `--to ` | End of interval (ISO timestamp) | — | | `--limit ` | Items per page when fetching from API | `200` | | `--input ` | Read from local JSON/NDJSON file instead of API | — | | `--burst` | Detect 5-minute windows above 3× the average rate | `false` | | `--heatmap` | Show day-of-week × hour-of-day heatmap | `false` | | `-f, --format ` | Output format: `json` or `table` | `table` | | `--env ` | Custom .env file path | `.env` | ## Source Names The `--source` filter accepts these values: - `logging` - `audit-log` - `rlimit` ## Hint Normalization With `--normalize-hints` (or always in `stats`), hints are normalized by stripping dynamic values: 1. File IDs in file-id context → `` 2. UUIDs (8-4-4-4-12 hex) → `` 3. Numeric IDs in parentheses `(12345)` → `()` 4. Elapsed times (`7.5s`, `2m3.027s`) → `` 5. URIs (`https://...`) → `` 6. Unicode quotes and whitespace normalized ## Examples ### List recent errors ```bash ./scripts/error-reports.mjs list --limit 10 ``` ### Time-range query (today) ```bash ./scripts/error-reports.mjs list --from 2026-07-23T00:00:00Z --to 2026-07-23T23:59:59Z --all ``` ### Stream all errors as NDJSON ```bash ./scripts/error-reports.mjs list --all --format ndjson > errors.ndjson ``` ### Save to file with --output ```bash ./scripts/error-reports.mjs list --all --format ndjson -o errors.ndjson ./scripts/error-reports.mjs list --format json -o errors.json ``` ### Filter by source ```bash ./scripts/error-reports.mjs list --source audit-log --limit 20 ``` ### Filter by kind ```bash ./scripts/error-reports.mjs list --kind exception-page ``` ### Filter by tenant ```bash ./scripts/error-reports.mjs list --tenant production ``` ### Filter by version ```bash ./scripts/error-reports.mjs list --version 2.1.0 ``` ### Search by hint (partial match) ```bash ./scripts/error-reports.mjs list --hint "NullPointerException" ``` ### Fetch all errors with pagination ```bash ./scripts/error-reports.mjs list --all ``` ### Get specific error by ID ```bash ./scripts/error-reports.mjs get --id 550e8400-e29b-41d4-a716-446655440000 ``` ### Output as JSON ```bash ./scripts/error-reports.mjs list --limit 5 --format json ``` ### Combine filters ```bash ./scripts/error-reports.mjs list --source audit-log --kind exception-page --tenant production --limit 50 ``` ### Stats with burst and heatmap analysis ```bash ./scripts/error-reports.mjs stats --from 2026-07-23T00:00:00Z --to 2026-07-23T23:59:59Z --burst --heatmap ``` ### Stats from file ```bash ./scripts/error-reports.mjs stats --input errors.json ``` ### Stats from pipe ```bash ./scripts/error-reports.mjs list --all --format json | ./scripts/error-reports.mjs stats ``` ## Output Formats ### Table (default) Human-readable table format for terminal display. With `--all`, rows stream as they arrive. ### JSON Single page: `{items: [...], nextSince, nextId}`. `--all` cannot be combined with `--format json`; use `--format ndjson` for streaming. ### NDJSON One JSON object per line, always streaming. Pipe-friendly: `| jq -c '.hint'`, `| wc -l`. ## Pagination The server returns items in **ascending** order (oldest first). Cursor pagination uses `--since` / `--since-id` to fetch the next page of newer items. ### Manual pagination Use `--since` and `--since-id` with values from `nextSince` and `nextId` in the response: ```bash ./scripts/error-reports.mjs list --limit 50 # Use nextSince and nextId from response ./scripts/error-reports.mjs list --limit 50 --since "2026-01-20T10:29:00Z" --since-id "next-uuid" ``` ### Automatic pagination Use `--all` to fetch all pages automatically (streams output): ```bash ./scripts/error-reports.mjs list --all ``` ### Time-range queries Use `--from` and `--to` to bound the query. These map to the server's `--since` and `--until` parameters: ```bash ./scripts/error-reports.mjs list --from 2026-07-20T00:00:00Z --to 2026-07-23T23:59:59Z --all ``` ## Key principles - **Authentication required** - Uses access token with `error-reports:read` permission - **API endpoint configurable** - Set via `PENPOT_API_URI` in `.env` file - **Table is default format** - Use `--format json` for structured JSON, `--format ndjson` for streaming - **Streaming with --all** - Items print as they arrive, no buffering. Use `--format ndjson` or `--format table`; `--all --format json` is rejected. - **Filters are combinable** - All filter options can be used together - **Both flag formats supported** - `--option=value` and `--option value` both work - **Ascending order** - Server returns oldest items first (changed from DESC) ## Error handling The tool provides helpful error messages for common issues: - **Missing configuration**: Shows setup instructions for `.env` file - **Authentication errors (401)**: Indicates invalid or expired token - **Authorization errors (403)**: Indicates missing `error-reports:read` permission - **RPC errors**: Displays error code and message from the API ## Integration with other scripts - **jq**: Pipe NDJSON output to `jq` for further processing ```bash ./scripts/error-reports.mjs list --all --format ndjson | jq -c '{id, hint}' ``` - **stats from pipe**: Fetch data once, compute stats ```bash ./scripts/error-reports.mjs list --all --format ndjson | ./scripts/error-reports.mjs stats ``` - **stats from NDJSON pipe**: Works with NDJSON format too ```bash ./scripts/error-reports.mjs list --all --format ndjson | ./scripts/error-reports.mjs stats ``` - **grep/search**: Filter output by specific patterns - **--output**: Save to file without shell redirection ```bash ./scripts/error-reports.mjs list --all --format ndjson -o errors.ndjson ```