Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,29 @@ breaking is [written down in CONTRIBUTING.md](CONTRIBUTING.md#compatibility) —
command names, flags, the two output modes and the exit codes are promises;
the Mapbox APIs' own response bodies are not.

## Unreleased

### Added

- Paginated listings now say when there is more to fetch. A response the API
paged answers with one page, and the CLI prints the flags that fetch the
next one — "More results: add `--limit 2 --start …` for the next page" —
on stderr in both output modes. Before this the extra pages were
unreachable: the API signals them in a `Link` header, which nothing read,
so `-o text` and `-o json` both looked complete. `--id` on a paged listing
now also distinguishes "not on this page" from "does not exist", and says
how to look further. Following the pages is still the caller's job; there
is no `--all` yet.

- Failures now carry the response's request id, which is what Mapbox support
needs to find one request in their logs. In practice that is CloudFront's
`x-amz-cf-id`, which every Mapbox response carries; a service sending its
own `x-request-id` is preferred when one does. Present on every failure
under `-o json` as `request_id`; printed under `-o text` for a 5xx only,
where the server is at fault and there is nothing the caller can do about
it. `mapbox agent-skills` is exempt — it fetches from GitHub, whose request
id Mapbox support cannot look up.

## 0.1.8 - 2026-09-14

Initial beta release. The next release is `0.2.0`.
Expand Down
77 changes: 75 additions & 2 deletions docs/commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -571,6 +571,55 @@ map.png: PNG image data, 600 x 400
</td></tr>
</table>

#### One page at a time

Several listings are paginated by the API, which returns one page and a
`Link` header naming the next. **The CLI says so rather than leaving the
result looking complete**, and names the flags that fetch the next page:

```
$ mapbox accounts list-tokens --username user --limit 2
ID NOTE CREATED USAGE
cmtoken00000000000000001a CI deploy key 2026-09-04 sk
cmtoken00000000000000002b Local dev 2026-09-03 pk

Tips:
`-o json` for the response as the API sent it.
To see one row: add `--id cmtoken00000000000000001a`
More results: add `--limit 2 --start cmtoken00000000000000002b` for the next page.
```

Under `-o json` the same note is the only thing printed to stderr on a
success, and as the lone tip it takes the singular form:

```
$ mapbox accounts list-tokens --username user --limit 2 -o json > page1.json
Tip: More results: add `--limit 2 --start cmtoken00000000000000002b` for the next page.
```

The flags are derived from the response, not hardcoded: whatever the spec
calls an operation's paging parameters is what the line names. The access
token is never among them, even though the API echoes it back in that header.

Two details worth knowing:

- **The note goes to stderr in both modes**, including `-o json`. The result
is just as partial there, and the API's own document cannot carry the fact
without an envelope this CLI has promised not to add — so a `-o json`
consumer reading stdout alone is unaffected, and one watching stderr is
told. There is no `--all` yet; following the pages is the caller's job,
and [#117](https://github.com/mapbox/mapbox-cli-private/issues/117) tracks
changing that.
- **`--id` searches the page it was given.** On a paginated listing a miss
means "not on this page", which is not the same as "does not exist", so
the error says which and how to look further:

```
Error: No row has the id `cmtoken00000000000000009z`.
Fix: This is one page of results, so the id may be on a later one. Add
`--start cmtoken00000000000000002b --limit 2` to search the next page.
```

---

## Accounts
Expand All @@ -593,8 +642,8 @@ Lists the access tokens for an account. Secret (`sk`) entries omit the
| `--usage <pk\|sk\|tk>` | Only tokens of that kind. |
| `--default` | Only the account's default token. |

Results are paginated: a `Link` header with `rel="next"` signals more, and
its `start` value is what `--start` wants.
Results are paginated. When more exist the CLI prints the `--start` value to
continue from — see [One page at a time](#one-page-at-a-time).

#### Examples

Expand Down Expand Up @@ -3420,6 +3469,30 @@ answers "was this computed?".
| `fix` | One line: why it failed, and what would make it work. |
| `next_actions` | Commands to run, and nothing else — no prose to strip before running one. |
| `docs` | The pages that bear on the failure: the command's own, plus the tokens page when it was the credential that was refused. |
| `request_id` | The response's request id, for quoting to Mapbox support. |

`request_id` is the one field whose two renderings differ on purpose. Under
`-o json` it is there on **every** failure that carried one, whatever the
status, because a caller logging failures wants it on all of them and a field
costs nothing to ignore. Under `-o text` it is printed for a **5xx only**:

```
Error: Internal server error (HTTP 500)
Fix: The service failed rather than refusing the request. Retry, and check https://status.mapbox.com if it persists.
Request ID: 01JC8K3Q7V9XZ4M2 (quote this to Mapbox support)
```

That is the failure a person escalates, and the id is what lets support find
the request in their logs. A 404 on a mistyped id is the reader's own to fix,
so an id under it would be noise on the common case.

The id is whatever identified the response: `x-request-id` from a service
that sends one, and otherwise `x-amz-cf-id`, the CloudFront id every Mapbox
response carries. Quote it as printed — support can trace either.

`mapbox agent-skills` is the one command whose failures carry no
`request_id`, and deliberately: it fetches from GitHub, which identifies
requests with its own header that Mapbox support cannot look up.

The advice is keyed on the HTTP status, with the command filling in what only
it knows — and it is read off the parsed spec, so a suggestion can only name
Expand Down
4 changes: 4 additions & 0 deletions src/account_usage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -614,12 +614,16 @@ fn fetch(
.map_err(|e| executor::transport_failure("Request failed", e))?;

let status = response.status();
// Before `text()` consumes the response: a 5xx here is worth escalating,
// and this is the only thing that lets support find the request.
let request_id = executor::request_id(response.headers());
let text = response
.text()
.map_err(|e| executor::transport_failure("Failed to read response", e))?;

if !status.is_success() {
return Err(CliError::http(status.as_u16(), &text)
.with_request_id(request_id)
.with_remedy(remedy_for(status.as_u16()))
.into());
}
Expand Down
6 changes: 5 additions & 1 deletion src/auth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1032,12 +1032,16 @@ fn verify_token(token: &str, debug: bool, timeout: Option<Duration>) -> Result<V
.map_err(|e| crate::executor::transport_failure("Token check failed", e))?;

let status = response.status();
// Before `text()` consumes the response — see `executor::request_id`.
let request_id = crate::executor::request_id(response.headers());
let body = response
.text()
.map_err(|e| crate::executor::transport_failure("Failed to read the token check", e))?;

if !status.is_success() {
return Err(CliError::http(status.as_u16(), &body).into());
return Err(CliError::http(status.as_u16(), &body)
.with_request_id(request_id)
.into());
}

serde_json::from_str(&body).context("Invalid JSON from the token check endpoint")
Expand Down
Loading