Skip to content

feat(cli): carry plan-gate entitlement on machine-mode errors - #7271

Merged
pamelachia merged 8 commits into
developfrom
pamela/growth-1050-entitlement-json-errors
Oct 26, 2026
Merged

pamelachia merged 8 commits into
developfrom
pamela/growth-1050-entitlement-json-errors

Conversation

@pamelachia

@pamelachia pamelachia commented Oct 21, 2026 •

Copy link
Copy Markdown
Contributor

Plan-gated Management API denials already return { error: { code: "entitlement_required", feature, upgrade_url } }. In --output-format json and stream-json that signal only survives as escaped text inside error.message, so an agent has to parse prose to learn that an upgrade is needed and where. I added a structured entitlement to the machine-mode error object for every command whose status errors go through mapHttpError.

{"_tag":"Error","error":{"code":"DomainsUnexpectedStatusError","message":"...","entitlement":{"feature":"custom_domain","upgrade_url":"https://supabase.com/dashboard/org/<slug>/billing"}}}

Changed:

  • Machine-mode errors carry the plan gate: on a 4xx, mapHttpError decodes the untruncated body and, when it is the plan-gate envelope, attaches entitlement: { feature, upgrade_url } to the status error. normalizeCliError passes it into the json and stream-json error object, so commands with no per-command upgrade wiring (branches unpause) get it too.
  • Handlers that rebuild a mapped error keep it: branches create/update, vanity-subdomains activate/check-availability and sso list/add/update/remove forward the field, and Output.fail declares it in its type.
  • Envelope values are validated in one place: I moved the parser into http-errors.ts and suggestUpgrade reuses it. feature must be a plain key and upgrade_url an http(s) URL without credentials, emitted as the parsed URL so control and bidi characters arrive percent-encoded; anything else counts as no envelope.

Note: text output and cli_upgrade_suggested are unchanged for well-formed envelopes. The per-command text hint stays until I move the hint and the event onto the same central path in a follow-up.

Linear

  • part of GROWTH-1050

@pamelachia pamelachia self-assigned this Oct 21, 2026
@pamelachia
pamelachia marked this pull request as ready for review October 23, 2026 08:15
@pamelachia
pamelachia requested a review from a team as a code owner October 23, 2026 08:15
…-entitlement-json-errors

# Conflicts:
#	apps/cli/src/shared/output/normalize-error.ts
@pamelachia

Copy link
Copy Markdown
Contributor Author

Manual check from source against the Management API on a free-plan org:

  • supabase domains get --project-ref <ref> --output-format json, expect entitlement: { feature: "custom_domain", upgrade_url: ".../org/<slug>/billing" } on the error object
  • same with --output-format stream-json, expect entitlement on the type: "error" event
  • SUPABASE_EXPERIMENTAL=1 supabase vanity-subdomains get --project-ref <ref> --output-format json, expect entitlement.feature: "vanity_subdomain"
  • supabase domains get --project-ref <ref> --agent no, expect the text-mode "Upgrade your plan: .../billing" hint
  • stub API returning the envelope with upgrade_url: "javascript:alert(1)", expect no entitlement; a plain 403, expect no entitlement

@Coly010 Coly010 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, a couple of small non-blocking asks inline

Comment thread apps/cli/AGENTS.md Outdated
Comment thread apps/cli/src/shared/output/normalize-error.ts
@pamelachia
pamelachia added this pull request to the merge queue Oct 26, 2026
Merged via the queue into develop with commit e2a524f Oct 26, 2026
52 checks passed
@pamelachia
pamelachia deleted the pamela/growth-1050-entitlement-json-errors branch October 26, 2026 07:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants