Skip to content

fix(init): require auth upfront and fix prompt/spinner ordering#610

Open
betegon wants to merge 9 commits intomainfrom
fix/init-prompts-before-spinner
Open

fix(init): require auth upfront and fix prompt/spinner ordering#610
betegon wants to merge 9 commits intomainfrom
fix/init-prompts-before-spinner

Conversation

@betegon
Copy link
Copy Markdown
Member

@betegon betegon commented Mar 30, 2026

Summary

Two bugs in sentry init:

1. No auth flow when unauthenticated

Running sentry init without being logged in would silently fail with "Connection failed" (HTTP 401 from Mastra, swallowed by the wizard's catch block) instead of triggering the login flow. Added refreshToken() as the first thing in the command so unauthenticated users get the login prompt immediately — same as every other command that requires auth.

2. Prompts appearing while spinner is running

With multiple Sentry orgs, the org select() prompt appeared while the spinner was still active. Clack's spinner runs on setInterval, so it kept writing animation frames below the prompt, garbling the display:

◆  Which organization should the project be created in?
│  ● bete-dev (bete-dev)
│  ...
└
◒  Creating Sentry project "nextjs-sentry-test" (javascript-nextjs).

Clack's design is prompts-first, then tasks. The org resolution and existing-project detection were buried inside createSentryProject (a local-op that runs during the spinner). Moved them into a prompt phase in runWizard() that completes before spin.start().

Test plan

  • Run sentry init while not logged in — login flow triggers before any wizard UI appears
  • Run sentry init with multiple orgs — org prompt appears cleanly before the spinner
  • Run with --org — prompt skipped, spinner starts immediately
  • Run in a project with an existing Sentry DSN — "use existing?" prompt appears before spinner
  • Run with --yes — existing project auto-selected, no prompt shown

Clack's design is prompts-first, then tasks. The previous code called
select() inside createSentryProject (a local-op) while the spinner was
still running, causing the spinner's setInterval to write output below
the active prompt's bottom border.

Fix: resolve org and detect existing project before spin.start() in
runWizard(). Since options.org is now always set by the time the spinner
starts, createSentryProject's interactive prompts are automatically
skipped (they already guard on options.org).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Mar 30, 2026

Semver Impact of This PR

🟢 Patch (bug fixes)

📋 Changelog Preview

This is how your changes will appear in the changelog.
Entries from this PR are highlighted with a left border (blockquote style).


New Features ✨

  • (telemetry) Add seer.outcome span tag for Seer command metrics by BYK in #609
  • (upgrade) Show changelog summary during CLI upgrade by BYK in #594

Bug Fixes 🐛

Upgrade

  • Prevent spinner freeze during delta patch application by BYK in #608
  • Indent changelog, add emoji to heading, hide empty sections by BYK in #604

Other

  • (dashboard) Reject MRI queries with actionable tracemetrics guidance by BYK in #601
  • (init) Require auth upfront and fix prompt/spinner ordering by betegon in #610
  • (skill) Avoid unnecessary auth, reinforce auto-detection, fix field examples by BYK in #599
  • 2 bug fixes — subcommand crash, negative span depth, pagination JSON parse by cursor in #607

Documentation 📚

  • (skill) Document dashboard widget constraints and deprecated datasets by BYK in #605
  • Fix documentation gaps and embed skill files at build time by cursor in #606

Internal Changes 🔧

  • Regenerate skill files and command docs by github-actions[bot] in 664362ca

🤖 This preview updates automatically when you update the PR.

@betegon betegon marked this pull request as ready for review March 30, 2026 18:07
When a user cancelled the org selection prompt, resolveOrgSlug returned
{ ok: false, error: "Organisation selection cancelled." }, which the
pre-spinner handler treated as a hard failure (exit code 1, "Setup
failed.").

Fix: throw WizardCancelledError on cancel (consistent with
abortIfCancelled) and catch it in the pre-spinner phase for a graceful
exit (code 0, "Setup cancelled.").

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
betegon and others added 2 commits March 30, 2026 22:09
Re-throwing non-WizardCancelledError exceptions from resolveOrgSlug
caused unhandled rejections on network or auth failures. Handle them
with log.error + cancel + exit 1, consistent with the rest of the
wizard error handling.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Without this, running `sentry init` while unauthenticated would either:
- silently fail with "Connection failed" (HTTP 401 from the Mastra API
  swallowed by the wizard's catch block), or
- show org/project prompts before failing

Now `refreshToken()` is called as the first thing in the command, so an
unauthenticated user gets the interactive login flow immediately —
consistent with every other command that requires auth.
@betegon betegon changed the title fix(init): move org/project prompts before spinner fix(init): require auth upfront and fix prompt/spinner ordering Mar 30, 2026
…ection

- Stop spinner before throwing AuthError so the terminal isn't garbled
  when the login flow retakes control
- Use a separate 'resolvedOrg' field for orgs auto-resolved in the prompt
  phase, keeping options.org as the user-provided value so createSentryProject's
  bare-slug existing-project check (options.project && !options.org) stays correct
- Tighten is401() from .includes('401') to .includes('status: 401') to
  avoid false positives on error messages containing '4010ms' etc.
betegon added 2 commits March 30, 2026 23:03
…ng-project prompt

Two follow-up bugs from moving prompts before the spinner:

1. When running 'sentry init' with no args and an existing DSN is found,
   the user is prompted 'use existing or create new?'. If they pick 'create new',
   createSentryProject re-shows the same prompt during the spinner because the
   check (options.org || options.project) didn't account for resolvedOrg being
   set. Fix: add resolvedOrg to the guard so the pre-spinner result is respected.

2. Running 'sentry init my-app' (bare slug, no org) still showed the existing-
   project prompt inside the spinner because the bare-slug check in
   createSentryProject (options.project && !options.org) ran after the spinner
   started. Fix: perform the check in the pre-spinner phase after resolving org,
   set options.org if the user picks 'use existing', and skip the check in
   createSentryProject when resolvedOrg is set.
All interactive prompts (existing project detection, org resolution,
bare-slug existing-project check) are now handled in runWizard's
pre-spinner phase. By the time createSentryProject runs, org is always
set via options.org or options.resolvedOrg — the previous fallback
branches are unreachable.

Also removes the now-unused promptForExistingProject helper.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Comment on lines +366 to +375
if (options.project) {
const slug = slugify(options.project);
const existing = slug
? await tryGetExistingProject(orgResult, slug).catch(() => null)
: null;
if (existing?.ok) {
if (yes) {
// --yes: silently use the existing project
options = { ...options, org: orgResult };
} else {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Bug: The code makes a redundant API call to tryGetExistingProject when initializing an existing project via a bare slug, fetching the same project details twice.
Severity: MEDIUM

Suggested Fix

Reuse the project data obtained from the first tryGetExistingProject call in wizard-runner.ts. Pass this data down to the createSentryProject function to avoid making the second, redundant API call in local-ops.ts.

Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent.
Verify if this is a real issue. If it is, propose a fix; if not, explain why it's not
valid.

Location: src/lib/init/wizard-runner.ts#L366-L375

Potential issue: When initializing a project with a bare slug (e.g., `sentry init
<project-name>`) that already exists, the application makes two identical API calls to
`tryGetExistingProject`. The first call occurs in `wizard-runner.ts:369` to check for
the project's existence before prompting the user. After the user confirms their choice,
a second, redundant call is made in `local-ops.ts:831`. The result from the first API
call is discarded, leading to an unnecessary network request and a performance
degradation due to the API N+1 pattern.

Copy link
Copy Markdown
Contributor

@cursor cursor bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

// All interactive prompts (existing project detection, org resolution, bare
// slug existing-project check) are handled in runWizard's pre-spinner phase.
// By the time we reach here, options.org or options.resolvedOrg is always set.
const orgSlug = (options.org ?? options.resolvedOrg) as string;
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.

Unsafe as string cast hides potential undefined orgSlug

Medium Severity

(options.org ?? options.resolvedOrg) as string silently casts a potentially undefined value to string. Both fields are optional in WizardOptions, and the invariant that one is always set relies on the prompt phase in runWizard having executed first. If createSentryProject is ever reached without the prompt phase (e.g., future refactoring, or a code path bypassing runWizard), orgSlug becomes undefined at runtime, causing silent failures in downstream API calls like resolveOrCreateTeam and createProject with an undefined org.

Additional Locations (1)
Fix in Cursor Fix in Web

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