From 918b6a204519b86ac31ab8ec710266934fe8308f Mon Sep 17 00:00:00 2001 From: betegon Date: Fri, 29 May 2026 16:42:24 +0200 Subject: [PATCH 1/2] fix(init): fall back to no-platform on 400 invalid platform from registry The Sentry release registry added new JS SDK entries (sentry.javascript.hono, elysia, nitro, effect, etc.) on May 28 as part of the 10.55.0 release. The platform-matcher agent now returns these keys for matching frameworks, but the Sentry project-creation API doesn't yet accept the derived platform slugs (e.g. "javascript-hono"), returning 400 {"platform":["Invalid platform"]}. When that happens, retry project creation without the platform field so the project is still created and the wizard continues. Capture the exception in Sentry so we can track which slugs need to be added to the API allowlist. Co-Authored-By: Claude Sonnet 4.6 (1M context) --- src/lib/init/tools/create-sentry-project.ts | 71 ++++++++++++++++----- 1 file changed, 55 insertions(+), 16 deletions(-) diff --git a/src/lib/init/tools/create-sentry-project.ts b/src/lib/init/tools/create-sentry-project.ts index f708759bd..400080a2f 100644 --- a/src/lib/init/tools/create-sentry-project.ts +++ b/src/lib/init/tools/create-sentry-project.ts @@ -7,6 +7,7 @@ * lack team:write. */ +import { captureException } from "@sentry/node-core/light"; import { createProjectWithAutoTeam, createProjectWithDsn, @@ -58,6 +59,37 @@ async function resolveProjectCreation(opts: { const { org, name, team, suppressFallback, slugHint } = opts; // Coerce null → undefined: CreateProjectBody.platform is string | undefined. const platform = opts.platform ?? undefined; + + const withPlatformFallback = async ( + fn: (p: string | undefined) => Promise + ): Promise => { + try { + return await fn(platform); + } catch (err) { + // The registry may include SDK keys whose derived platform slug (e.g. + // "javascript-hono") is not yet in the Sentry API's allowed platform + // list. Retry without a platform so the project is still created, and + // capture to track which slugs need to be added to the API allowlist. + if ( + err instanceof ApiError && + err.status === 400 && + platform && + err.detail?.includes("Invalid platform") + ) { + captureException(err, { + extra: { + attemptedPlatform: platform, + projectName: name, + apiResponseDetail: err.detail, + apiStatus: err.status, + }, + }); + return await fn(undefined); + } + throw err; + } + }; + try { const teamSlug = team ? team @@ -67,16 +99,18 @@ async function resolveProjectCreation(opts: { usageHint: "sentry init", }) ).slug; - const result = await createProjectWithDsn(org, teamSlug, { - name, - platform, + return await withPlatformFallback(async (p) => { + const result = await createProjectWithDsn(org, teamSlug, { + name, + platform: p, + }); + return { + projectSlug: result.project.slug, + projectId: result.project.id, + dsn: result.dsn ?? "", + url: result.url, + }; }); - return { - projectSlug: result.project.slug, - projectId: result.project.id, - dsn: result.dsn ?? "", - url: result.url, - }; } catch (innerError) { // Fall back to org-scoped endpoint on 403, unless the fallback is suppressed // (explicit --team means the 403 is meaningful feedback, not a permission gap). @@ -92,13 +126,18 @@ async function resolveProjectCreation(opts: { if (innerError.detail?.includes(MEMBER_PROJECT_CREATION_DISABLED_DETAIL)) { throw innerError; } - const result = await createProjectWithAutoTeam(org, { name, platform }); - return { - projectSlug: result.project.slug, - projectId: result.project.id, - url: result.url, - dsn: result.dsn ?? "", - }; + return await withPlatformFallback(async (p) => { + const result = await createProjectWithAutoTeam(org, { + name, + platform: p, + }); + return { + projectSlug: result.project.slug, + projectId: result.project.id, + url: result.url, + dsn: result.dsn ?? "", + }; + }); } } From b7e990807b6e3aacf7a62a54c0b5cb7da5bd012e Mon Sep 17 00:00:00 2001 From: betegon Date: Fri, 29 May 2026 17:05:53 +0200 Subject: [PATCH 2/2] docs(init): clarify 403 fallback handles both initial and retry failures Co-Authored-By: Claude Sonnet 4.6 (1M context) --- src/lib/init/tools/create-sentry-project.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/lib/init/tools/create-sentry-project.ts b/src/lib/init/tools/create-sentry-project.ts index 400080a2f..cd26d3b27 100644 --- a/src/lib/init/tools/create-sentry-project.ts +++ b/src/lib/init/tools/create-sentry-project.ts @@ -114,6 +114,9 @@ async function resolveProjectCreation(opts: { } catch (innerError) { // Fall back to org-scoped endpoint on 403, unless the fallback is suppressed // (explicit --team means the 403 is meaningful feedback, not a permission gap). + // Note: a 403 can originate from either the initial createProjectWithDsn call + // or from the platform-less retry inside withPlatformFallback — both mean the + // caller lacks team:write, so the org-scoped fallback is correct in either case. if ( !(innerError instanceof ApiError && innerError.status === 403) || suppressFallback