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

@wyattjohwyattjoh commented May 13, 2026

Copy link
Copy Markdown
Contributor

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

wyattjoh commented May 13, 2026

Copy link
Copy Markdown
ContributorAuthor

@changeset-bot

changeset-botBot commented May 13, 2026

Copy link
Copy Markdown

🦋 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
NameType
clerkMinor

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

@wyattjohwyattjoh changed the title wyattjoh/deploy error recoveryfeat(deploy): recover from production_instance_exists and surface plan-feature errorsMay 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
wyattjohforce-pushed the wyattjoh/deploy-error-recovery branch from ce49854 to fae8e67CompareMay 19, 2026 13:55
@wyattjoh
wyattjohforce-pushed the wyattjoh/deploy branch 3 times, most recently from a3d8b46 to 7e6819cCompareMay 21, 2026 15:26
@wyattjoh

Copy link
Copy Markdown
ContributorAuthor

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 deleted the wyattjoh/deploy-error-recovery branch May 21, 2026 15:56
Sign up for freeto 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

@wyattjoh
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
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

@wyattjohwyattjoh commented May 13, 2026

Copy link
Copy Markdown
Contributor

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

wyattjoh commented May 13, 2026

Copy link
Copy Markdown
ContributorAuthor

@changeset-bot

changeset-botBot commented May 13, 2026

Copy link
Copy Markdown

🦋 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
NameType
clerkMinor

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

@wyattjohwyattjoh changed the title wyattjoh/deploy error recoveryfeat(deploy): recover from production_instance_exists and surface plan-feature errorsMay 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
wyattjohforce-pushed the wyattjoh/deploy-error-recovery branch from ce49854 to fae8e67CompareMay 19, 2026 13:55
@wyattjoh
wyattjohforce-pushed the wyattjoh/deploy branch 3 times, most recently from a3d8b46 to 7e6819cCompareMay 21, 2026 15:26
@wyattjoh

Copy link
Copy Markdown
ContributorAuthor

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 deleted the wyattjoh/deploy-error-recovery branch May 21, 2026 15:56
Sign up for freeto 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

@wyattjoh
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
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

@wyattjohwyattjoh commented May 13, 2026

Copy link
Copy Markdown
Contributor

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

wyattjoh commented May 13, 2026

Copy link
Copy Markdown
ContributorAuthor

@changeset-bot

changeset-botBot commented May 13, 2026

Copy link
Copy Markdown

🦋 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
NameType
clerkMinor

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

@wyattjohwyattjoh changed the title wyattjoh/deploy error recoveryfeat(deploy): recover from production_instance_exists and surface plan-feature errorsMay 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
wyattjohforce-pushed the wyattjoh/deploy-error-recovery branch from ce49854 to fae8e67CompareMay 19, 2026 13:55
@wyattjoh
wyattjohforce-pushed the wyattjoh/deploy branch 3 times, most recently from a3d8b46 to 7e6819cCompareMay 21, 2026 15:26
@wyattjoh

Copy link
Copy Markdown
ContributorAuthor

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 deleted the wyattjoh/deploy-error-recovery branch May 21, 2026 15:56
Sign up for freeto 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

@wyattjoh
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
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

@wyattjohwyattjoh commented May 13, 2026

Copy link
Copy Markdown
Contributor

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

wyattjoh commented May 13, 2026

Copy link
Copy Markdown
ContributorAuthor

@changeset-bot

changeset-botBot commented May 13, 2026

Copy link
Copy Markdown

🦋 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
NameType
clerkMinor

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

@wyattjohwyattjoh changed the title wyattjoh/deploy error recoveryfeat(deploy): recover from production_instance_exists and surface plan-feature errorsMay 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
wyattjohforce-pushed the wyattjoh/deploy-error-recovery branch from ce49854 to fae8e67CompareMay 19, 2026 13:55
@wyattjoh
wyattjohforce-pushed the wyattjoh/deploy branch 3 times, most recently from a3d8b46 to 7e6819cCompareMay 21, 2026 15:26
@wyattjoh

Copy link
Copy Markdown
ContributorAuthor

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 deleted the wyattjoh/deploy-error-recovery branch May 21, 2026 15:56
Sign up for freeto 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

@wyattjoh
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
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

@wyattjohwyattjoh commented May 13, 2026

Copy link
Copy Markdown
Contributor

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

wyattjoh commented May 13, 2026

Copy link
Copy Markdown
ContributorAuthor

@changeset-bot

changeset-botBot commented May 13, 2026

Copy link
Copy Markdown

🦋 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
NameType
clerkMinor

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

@wyattjohwyattjoh changed the title wyattjoh/deploy error recoveryfeat(deploy): recover from production_instance_exists and surface plan-feature errorsMay 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
wyattjohforce-pushed the wyattjoh/deploy-error-recovery branch from ce49854 to fae8e67CompareMay 19, 2026 13:55
@wyattjoh
wyattjohforce-pushed the wyattjoh/deploy branch 3 times, most recently from a3d8b46 to 7e6819cCompareMay 21, 2026 15:26
@wyattjoh

Copy link
Copy Markdown
ContributorAuthor

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 deleted the wyattjoh/deploy-error-recovery branch May 21, 2026 15:56
Sign up for freeto 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

@wyattjoh
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
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

@wyattjohwyattjoh commented May 13, 2026

Copy link
Copy Markdown
Contributor

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

wyattjoh commented May 13, 2026

Copy link
Copy Markdown
ContributorAuthor

@changeset-bot

changeset-botBot commented May 13, 2026

Copy link
Copy Markdown

🦋 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
NameType
clerkMinor

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

@wyattjohwyattjoh changed the title wyattjoh/deploy error recoveryfeat(deploy): recover from production_instance_exists and surface plan-feature errorsMay 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
wyattjohforce-pushed the wyattjoh/deploy-error-recovery branch from ce49854 to fae8e67CompareMay 19, 2026 13:55
@wyattjoh
wyattjohforce-pushed the wyattjoh/deploy branch 3 times, most recently from a3d8b46 to 7e6819cCompareMay 21, 2026 15:26
@wyattjoh

Copy link
Copy Markdown
ContributorAuthor

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 deleted the wyattjoh/deploy-error-recovery branch May 21, 2026 15:56
Sign up for freeto 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

@wyattjoh
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
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

@wyattjohwyattjoh commented May 13, 2026

Copy link
Copy Markdown
Contributor

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

wyattjoh commented May 13, 2026

Copy link
Copy Markdown
ContributorAuthor

@changeset-bot

changeset-botBot commented May 13, 2026

Copy link
Copy Markdown

🦋 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
NameType
clerkMinor

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

@wyattjohwyattjoh changed the title wyattjoh/deploy error recoveryfeat(deploy): recover from production_instance_exists and surface plan-feature errorsMay 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
wyattjohforce-pushed the wyattjoh/deploy-error-recovery branch from ce49854 to fae8e67CompareMay 19, 2026 13:55
@wyattjoh
wyattjohforce-pushed the wyattjoh/deploy branch 3 times, most recently from a3d8b46 to 7e6819cCompareMay 21, 2026 15:26
@wyattjoh

Copy link
Copy Markdown
ContributorAuthor

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 deleted the wyattjoh/deploy-error-recovery branch May 21, 2026 15:56
Sign up for freeto 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

@wyattjoh
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
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

@wyattjohwyattjoh commented May 13, 2026

Copy link
Copy Markdown
Contributor

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

wyattjoh commented May 13, 2026

Copy link
Copy Markdown
ContributorAuthor

@changeset-bot

changeset-botBot commented May 13, 2026

Copy link
Copy Markdown

🦋 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
NameType
clerkMinor

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

@wyattjohwyattjoh changed the title wyattjoh/deploy error recoveryfeat(deploy): recover from production_instance_exists and surface plan-feature errorsMay 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
wyattjohforce-pushed the wyattjoh/deploy-error-recovery branch from ce49854 to fae8e67CompareMay 19, 2026 13:55
@wyattjoh
wyattjohforce-pushed the wyattjoh/deploy branch 3 times, most recently from a3d8b46 to 7e6819cCompareMay 21, 2026 15:26
@wyattjoh

Copy link
Copy Markdown
ContributorAuthor

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 deleted the wyattjoh/deploy-error-recovery branch May 21, 2026 15:56
Sign up for freeto 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

@wyattjoh