Skip to content

Commit 174bad6

Browse files
betegonclaude
andcommitted
fix(init): restore prefetch consumption and numeric ID fallback
Use resolveOrgPrefetched instead of calling resolveOrg directly so the background DSN scan from warmOrgDetection isn't wasted. Also guard against resolveOrg returning a raw numeric ID (when normalizeNumericOrg cache is cold and API refresh fails) by falling through to the interactive org picker, matching the original behavior. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent bf80e53 commit 174bad6

File tree

2 files changed

+33
-13
lines changed

2 files changed

+33
-13
lines changed

src/lib/init/local-ops.ts

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ import {
1616
tryGetPrimaryDsn,
1717
} from "../api-client.js";
1818
import { ApiError } from "../errors.js";
19-
import { resolveOrg } from "../resolve-target.js";
2019
import { resolveOrCreateTeam } from "../resolve-team.js";
2120
import { buildProjectUrl } from "../sentry-urls.js";
2221
import { slugify } from "../utils.js";
@@ -26,6 +25,7 @@ import {
2625
MAX_FILE_BYTES,
2726
MAX_OUTPUT_BYTES,
2827
} from "./constants.js";
28+
import { resolveOrgPrefetched } from "./prefetch.js";
2929
import type {
3030
ApplyPatchsetPayload,
3131
CreateSentryProjectPayload,
@@ -706,11 +706,18 @@ async function applyPatchset(
706706
return { ok: true, data: { applied } };
707707
}
708708

709+
/** Matches a bare numeric org ID extracted from a DSN (e.g. "4507492088676352"). */
710+
const NUMERIC_ORG_ID_RE = /^\d+$/;
711+
709712
/**
710713
* Resolve the org slug using the shared offline-first resolver, falling back
711714
* to interactive selection when multiple orgs are available.
712715
*
713-
* Resolution priority (via {@link resolveOrg}):
716+
* Uses the prefetch-aware helper from `./prefetch.ts` — if
717+
* {@link warmOrgDetection} was called earlier (by `init.ts`), the result is
718+
* already cached and returns near-instantly.
719+
*
720+
* Resolution priority (via `resolveOrg`):
714721
* 1. CLI `--org` flag
715722
* 2. `SENTRY_ORG` / `SENTRY_PROJECT` env vars
716723
* 3. Config defaults (SQLite)
@@ -725,9 +732,11 @@ export async function resolveOrgSlug(
725732
cwd: string,
726733
yes: boolean
727734
): Promise<string | LocalOpResult> {
728-
// Reuse the shared offline-first resolver (flags, env vars, defaults, DSN)
729-
const resolved = await resolveOrg({ cwd });
730-
if (resolved) {
735+
// normalizeNumericOrg inside resolveOrg may return a raw numeric ID when
736+
// the cache is cold and the API refresh fails. Numeric IDs break write
737+
// operations (project/team creation), so fall through to the org picker.
738+
const resolved = await resolveOrgPrefetched(cwd);
739+
if (resolved && !NUMERIC_ORG_ID_RE.test(resolved.org)) {
731740
return resolved.org;
732741
}
733742

test/lib/init/local-ops.create-sentry-project.test.ts

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,7 @@ describe("create-sentry-project", () => {
194194

195195
describe("resolveOrgSlug (called directly)", () => {
196196
test("single org fallback when resolveOrg returns null", async () => {
197-
resolveOrgSpy.mockResolvedValue(null);
197+
resolveOrgPrefetchedSpy.mockResolvedValue(null);
198198
listOrgsSpy.mockResolvedValue([
199199
{ id: "1", slug: "solo-org", name: "Solo Org" },
200200
]);
@@ -206,7 +206,7 @@ describe("create-sentry-project", () => {
206206
});
207207

208208
test("no orgs (not authenticated) returns error result", async () => {
209-
resolveOrgSpy.mockResolvedValue(null);
209+
resolveOrgPrefetchedSpy.mockResolvedValue(null);
210210
listOrgsSpy.mockResolvedValue([]);
211211

212212
const result = await resolveOrgSlug("/tmp/test", false);
@@ -218,7 +218,7 @@ describe("create-sentry-project", () => {
218218
});
219219

220220
test("multiple orgs + yes flag returns error with slug list", async () => {
221-
resolveOrgSpy.mockResolvedValue(null);
221+
resolveOrgPrefetchedSpy.mockResolvedValue(null);
222222
listOrgsSpy.mockResolvedValue([
223223
{ id: "1", slug: "org-a", name: "Org A" },
224224
{ id: "2", slug: "org-b", name: "Org B" },
@@ -235,7 +235,7 @@ describe("create-sentry-project", () => {
235235
});
236236

237237
test("multiple orgs + interactive select picks chosen org", async () => {
238-
resolveOrgSpy.mockResolvedValue(null);
238+
resolveOrgPrefetchedSpy.mockResolvedValue(null);
239239
listOrgsSpy.mockResolvedValue([
240240
{ id: "1", slug: "org-a", name: "Org A" },
241241
{ id: "2", slug: "org-b", name: "Org B" },
@@ -249,7 +249,7 @@ describe("create-sentry-project", () => {
249249
});
250250

251251
test("multiple orgs + user cancels select throws WizardCancelledError", async () => {
252-
resolveOrgSpy.mockResolvedValue(null);
252+
resolveOrgPrefetchedSpy.mockResolvedValue(null);
253253
listOrgsSpy.mockResolvedValue([
254254
{ id: "1", slug: "org-a", name: "Org A" },
255255
{ id: "2", slug: "org-b", name: "Org B" },
@@ -296,8 +296,8 @@ describe("create-sentry-project", () => {
296296
});
297297

298298
describe("resolveOrgSlug — resolveOrg integration", () => {
299-
test("returns org from resolveOrg when it resolves", async () => {
300-
resolveOrgSpy.mockResolvedValue({ org: "acme-corp" });
299+
test("returns org slug when resolveOrg resolves", async () => {
300+
resolveOrgPrefetchedSpy.mockResolvedValue({ org: "acme-corp" });
301301

302302
const result = await resolveOrgSlug("/tmp/test", false);
303303

@@ -306,7 +306,18 @@ describe("create-sentry-project", () => {
306306
});
307307

308308
test("falls through to listOrganizations when resolveOrg returns null", async () => {
309-
resolveOrgSpy.mockResolvedValue(null);
309+
resolveOrgPrefetchedSpy.mockResolvedValue(null);
310+
listOrgsSpy.mockResolvedValue([
311+
{ id: "1", slug: "solo-org", name: "Solo Org" },
312+
]);
313+
314+
const result = await resolveOrgSlug("/tmp/test", false);
315+
316+
expect(result).toBe("solo-org");
317+
});
318+
319+
test("numeric ID from resolveOrg falls through to org picker", async () => {
320+
resolveOrgPrefetchedSpy.mockResolvedValue({ org: "4507492088676352" });
310321
listOrgsSpy.mockResolvedValue([
311322
{ id: "1", slug: "solo-org", name: "Solo Org" },
312323
]);

0 commit comments

Comments
 (0)