Repository navigation
fix(cli): Route sentry api org requests to accounts on other servers - #1978
Merged
Merged
Conversation
#1976 routed `sentry api organizations/<org>/...` to the stored account that knows the org, but only among accounts on the server the request URL already named, so a self-hosted org stayed unreachable while a SaaS login was active. Pick the account before the URL is built: `getDefaultSdkConfig` takes the request path and runs `selectAccountForOrg` without a server limit, so the base URL follows the picked account's server. A full URL passed to `sentry api` and an explicit server still keep the request on that server. Co-Authored-By: BYK <126780+BYK@users.noreply.github.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
BYK
approved these changes
Nov 5, 2026
BYK
added a commit
to getsentry/sentry-api-schema
that referenced
this pull request
Nov 5, 2026
…BaseUrl (#130) This adds a per-request base URL hook to the generated client, so a host can send each org-scoped request to the org's region or server **before** the URL is built. Follow-up to #129, which gave `CredentialProvider.current` the path's scope. That only helps pick a credential for a URL that's already built. ```ts const sentry = createSentryClient({ baseUrl: "https://sentry.io", resolveBaseUrl: async ({ baseUrl, scope }) => scope ? await regionUrlFor(scope.organization) : baseUrl, }); await sentry.listOrganizationIssues({ path: { organization_id_or_slug: "acme" } }); // → https://us.sentry.io/api/0/organizations/acme/issues/ ``` ## What changes - **`Config.resolveBaseUrl`** (types `BaseUrlRequest` and `BaseUrlResolver`, exported from the root). It receives `{ baseUrl, url, scope? }`: - `baseUrl` is the configured base URL, or `''` when none is set. - `url` is the built path and query. - `scope` is `parseApiPathScope(url)`, and is left out for unscoped paths. Its result replaces `baseUrl` for that one request, with trailing slashes removed. It runs once per request, including `prepareRequest`, after `requestValidator` and before request interceptors. A per-call `resolveBaseUrl` replaces the configured one. If it throws, the request rejects with that error unchanged and `fetch` is never called. - The new step lives in `scripts/generate-base-url-resolver.mjs`. It patches the pinned Fetch client in the same way `generate-request-preparation.mjs` does. - **`parseApiPathScope`** moves to `lib/api-client/api-path-scope.ts`, a module with no imports, so the generated client can use it without bundling `errors.ts`. `api-path.ts` re-exports it, so the public API doesn't change. The host still owns region discovery, caching, and trust. The client only asks the host which base URL to use. ## Consumer The toolkit CLI will use this to replace `resolveOrgRegion` calls at each call site and the `getDefaultSdkConfig({ path })` workaround from getsentry/toolkit#1978. I built that change against a local build of this branch. CLI `tsc`, lint, and the affected tests pass. ## Tests `test/base-url-resolution.test.ts` covers: - An org-scoped operation goes to the resolved base URL. The resolver gets the base URL, path, query, and scope. - A project scope is passed through. Unscoped paths get no `scope` key. - Raw `prepareRequest` URLs are resolved, including when no `baseUrl` is configured. - Ordering: the resolver runs after validation, before interceptors, and once per request. - A per-call resolver overrides the configured one. - A resolver that throws rejects the request before any fetch. `pnpm build`, `pnpm typecheck`, and `pnpm test` (632 tests) pass. <!-- junior-request-attribution:start --> via @BYK. <!-- junior-request-attribution:end --> <!-- junior-session-footer:start --> <!-- junior-conversation-id:local%3Aweb%3Aad43917b182be622bccdd8f7 --> -- [View Session in Sentry](https://sentry.sentry.io/explore/conversations/local%3Aweb%3Aad43917b182be622bccdd8f7/?project=4510944073809921) <!-- junior-session-footer:end --> Co-authored-by: sentry-junior[bot] <264270552+sentry-junior[bot]@users.noreply.github.com> Co-authored-by: BYK <126780+BYK@users.noreply.github.com>
7 tasks done
This branch was previously deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Follow-up to #1976 (#1803).
#1976 routes
sentry api organizations/<org>/...to the stored account that knows<org>, but the transport picks the account only after the request URL is built. So it could only choose among accounts on that URL's server. For example, with a SaaS login active, a self-hosted org's account was never used.This picks the account before the URL is built:
getDefaultSdkConfigtakes an optionalpath. When the path names an org, it runsselectAccountForOrgwith no server limit before readinggetApiBaseUrl(). An account on another server brings its server along (followDefaultAccountHost), so the base URL follows it.prepareRawApiRequestpasses the endpoint aspathwhen nobaseUrlis given.sentry api(baseUrl), and an explicit server (SENTRY_URL/SENTRY_HOST,.sentryclirc). For these, the transport's existing same-server routing still applies.This stays in the CLI rather than
@sentry/api. Switching servers changes which origin receives the credential, and the transport intentionally doesn't rewrite a URL it has already built. Picking the server is the CLI's job, the same wayresolveOrgRegionalready does it for typed commands.Tests
In
account-routing.test.ts, the old "only consider accounts on the request's server" test becomes three:SENTRY_URLkeeps the request on that server.baseUrl) only considers accounts on its server.pnpm run tscandpnpm run lintpass. The CLI suite passes except for 6 permission-based tests that also fail onmainin my sandbox (completions, upgrade, resilient DB, code-scanner, file-changes).The docs fragment
apps/cli-docs/src/fragments/commands/auth.mdis updated to match.via @BYK.
--
View Session in Sentry