Skip to content

feat(deploy): recover from production_instance_exists and surface plan-feature errors#289

Closed
wyattjoh wants to merge 25 commits into
wyattjoh/deployfrom
wyattjoh/deploy-error-recovery
Closed

feat(deploy): recover from production_instance_exists and surface plan-feature errors#289
wyattjoh wants to merge 25 commits into
wyattjoh/deployfrom
wyattjoh/deploy-error-recovery

Conversation

@wyattjoh
Copy link
Copy Markdown
Contributor

@wyattjoh wyattjoh commented May 13, 2026

Summary

Adds two live-error recovery paths in clerk deploy and the small refactors that make them readable. When production-instance creation returns production_instance_exists (HTTP 400) the wizard now refetches the application, persists the existing production instance id, and jumps straight into the reconcile flow as if local state had been intact. When clone validation returns unsupported_subscription_plan_features (HTTP 402) the wizard rethrows a CliError that lists the unsupported features from error.meta.unsupported_features and points at the billing-plans docs.

Details

createProductionInstance is wrapped in a try/catch that branches on the new structured PlapiError.code. On production_instance_exists, reloadProductionState calls fetchApplication, finds the existing production instance, persists its id, and returns an updated DeployContext; the wizard then calls reconcileExistingDeploy and returns, so subsequent runs short-circuit. runValidateCloning is wrapped similarly to translate the plan-features error before any UI is rendered. A small guard rejects production-instance creation responses that come back without active_domain; this required loosening active_domain to nullable in ProductionInstanceResponse so callers must check.

Test plumbing adds two mock injection flags on clerk deploy: --testFailCreateProductionInstanceExists and --testFailValidateCloningUnsupportedFeatures=<feature-list>. The mock layer in commands/deploy/mock.ts produces the corresponding PLAPI error envelopes so the new code paths can be exercised end-to-end without hitting a real Platform API. The deploy README.md gains a "Recovery paths" section documenting both error codes and the wizard's response.

Test plan

  • bun run scripts/run-tests.ts --pattern 'packages/cli-core/src/commands/deploy/**' --concurrency 1
  • bun run typecheck
  • bun run lint
  • CI green

@wyattjoh
Copy link
Copy Markdown
Contributor Author

wyattjoh commented May 13, 2026

@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented May 13, 2026

🦋 Changeset detected

Latest commit: fae8e67

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
clerk Minor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@wyattjoh wyattjoh changed the title wyattjoh/deploy error recovery feat(deploy): recover from production_instance_exists and surface plan-feature errors May 13, 2026
wyattjoh added 25 commits May 19, 2026 07:55
Replace formatApiBody(body: string) with formatApiBody(error: ApiError),
deleting extractApiErrorCode, extractApiErrors, and formatSingleError.
The new formatStructuredError reads code/message/meta directly from the
parsed ApiError instance. The agent path builds ApiErrorEntry inline from
structured fields and surfaces clerkTraceId in verbose human mode.
Tests updated to construct ApiError instances and reflect single-error
output for multi-error bodies.
- Preserve completed providers when pausing OAuth setup mid-loop, so
  `clerk deploy --continue` can finish multi-provider stacks.
- Surface a warning for OAuth providers enabled in dev that the wizard
  does not yet support, instead of silently skipping them.
- Close the gutter as Paused (not Failed) when DNS verification times
  out, since the state is recoverable via --continue.
- Tighten the production-domain regex to reject malformed inputs like
  example..com or example-.com before they reach the API.
Move deploy lifecycle endpoint wrappers into the shared PLAPI client while routing the deploy wizard through a command-local adapter that defaults to mocked operations until the backend endpoints are ready.
…ures

When validateCloning returns HTTP 402 with code
unsupported_subscription_plan_features, rethrow a CliError whose message
lists the unsupported features from meta.unsupported_features and includes
a docs URL hint pointing at clerk.com/docs/billing/plans.
@wyattjoh wyattjoh force-pushed the wyattjoh/deploy-error-recovery branch from ce49854 to fae8e67 Compare May 19, 2026 13:55
@wyattjoh wyattjoh force-pushed the wyattjoh/deploy branch 3 times, most recently from a3d8b46 to 7e6819c Compare May 21, 2026 15:26
@wyattjoh
Copy link
Copy Markdown
Contributor Author

Closing in favor of #260. The error-recovery work (HTTP 409 production_instance_exists + HTTP 402 unsupported_subscription_plan_features) landed on the parent PR via the dedicated errors.ts mapping module (commands/deploy/errors.ts), which covers a broader set of PLAPI error codes than this branch originally proposed. The two remaining defensive cleanups from this branch — typing active_domain as nullable with a runtime guard, and dropping the unused is_secondary field — have been folded into #260.

@wyattjoh wyattjoh closed this May 21, 2026
@wyattjoh wyattjoh deleted the wyattjoh/deploy-error-recovery branch May 21, 2026 15:56
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.

1 participant