Print the platform's refusal rather than the JSON it arrived in #17

Merged
geusebio merged 1 commit from feature/surface-platform-refusals into main 2026-08-07 22:45:53 +00:00
Owner

A bundle upload that fails has been reporting the raw response body next to the
status line:

bundle upload failed: 402 Payment Required: {"error":"Deploying is paused: …"}

That was tolerable while every failure was a malformed manifest or a bad token.
The platform now refuses a deploy for want of credit, in prose it expects a
human to act on — the balance and where to top it up — and pasting the JSON
around that sentence buries the one part that matters.

The decoding already existed for the /cli routes. Extracted from do() into
apiErrorFrom() and reused, so UploadBundle returns an *APIError like
everything else: the message reads as written, and a caller can still branch on
the status.

sc: bundle upload failed: Deploying is paused: grey.ooo has a credit balance of
$0.00. Anything already running stays up. Top up at
http://…/organizations/…/billing to deploy again.

Verification

go build ./..., go vet ./..., gofmt, go test ./... — all clean, plus a
new TestUploadBundleSurfacesThePlatformsRefusal covering the 402 path.

Exercised against a live control plane running the server half
(Grey.ooo/Someones.Computer#310): the output above is the real binary against a
zeroed balance, and the same deploy went through once the balance was restored.

Depends on

Nothing — the server sends {"error": …} on every route this touches already.
The 402 it was written for arrives with Grey.ooo/Someones.Computer#310.

A bundle upload that fails has been reporting the raw response body next to the status line: ```text bundle upload failed: 402 Payment Required: {"error":"Deploying is paused: …"} ``` That was tolerable while every failure was a malformed manifest or a bad token. The platform now refuses a deploy for want of credit, in prose it expects a human to act on — the balance and where to top it up — and pasting the JSON around that sentence buries the one part that matters. The decoding already existed for the `/cli` routes. Extracted from `do()` into `apiErrorFrom()` and reused, so `UploadBundle` returns an `*APIError` like everything else: the message reads as written, and a caller can still branch on the status. ```text sc: bundle upload failed: Deploying is paused: grey.ooo has a credit balance of $0.00. Anything already running stays up. Top up at http://…/organizations/…/billing to deploy again. ``` ## Verification `go build ./...`, `go vet ./...`, `gofmt`, `go test ./...` — all clean, plus a new `TestUploadBundleSurfacesThePlatformsRefusal` covering the 402 path. Exercised against a live control plane running the server half (Grey.ooo/Someones.Computer#310): the output above is the real binary against a zeroed balance, and the same deploy went through once the balance was restored. ## Depends on Nothing — the server sends `{"error": …}` on every route this touches already. The 402 it was written for arrives with Grey.ooo/Someones.Computer#310.
Print the platform's refusal rather than the JSON it arrived in
All checks were successful
CI / build (pull_request) Successful in 3m29s
1c375b067f
A bundle upload that fails has been reporting the raw response body next to
the status line. That was tolerable while every failure was a malformed
manifest or a bad token, but the platform now refuses a deploy in prose it
expects a human to act on — a credit balance too low to start anything new,
and where to top it up — and pasting the JSON around that sentence buries the
one part that matters.

The decoding already existed for the /cli routes. Extracted from do() into
apiErrorFrom() and reused, so UploadBundle returns an *APIError like everything
else: the message reads as written, and a caller can still branch on the status.
Sign in to join this conversation.
No reviewers
No labels
in-progress
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
Grey.ooo/someones.computer_agent!17
No description provided.