Skip to content

ref(cli): Template telemetry endpoints with the API catalog's paths - #2229

Open
sentry-junior[bot] wants to merge 2 commits into
bt/code-mode-path-matcherfrom
fix/cli-telemetry-catalog-templates
Open

sentry-junior[bot] wants to merge 2 commits into
bt/code-mode-path-matcherfrom
fix/cli-telemetry-catalog-templates

Conversation

@sentry-junior

@sentry-junior sentry-junior Bot commented Nov 13, 2026

Copy link
Copy Markdown
Contributor

Stacked on #1807. It also carries #2213's commit, which is already on main but not on #1807's branch; it drops out of the diff once #1807 picks up main.

This follows up @BYK's suggestion on #2213: normalizeEndpoint now templates paths with the @sentry/api OpenAPI path templates and matchApiPathTemplate, instead of a regex list. generate-api-schema.ts writes the 173 unique paths (~14 KB) to src/generated/api-path-templates.json, next to idempotent-operations.json, so the CLI never loads the full catalog. The template that matches the most of the path wins. Any segment past that match that no template spells out becomes {id}, so a path outside the catalog doesn't leak. This covers cli_error.api_endpoint, api_error.endpoint, and the http.client span description and URL attributes. The install-prefix and /api/0 handling from #2213 are unchanged.

Placeholder names change. Existing cli_error.api_endpoint values change too: {org} becomes {organization_id_or_slug}, {project} becomes {project_id_or_slug}, and {id} becomes the catalog's name where there is one, such as {issue_id} or {event_id}. Nothing in this repo keys on the old form except error-reporting.test.ts, which this PR updates. In Sentry, none of the sentry/cli fingerprint rules, issue alerts, or metric alerts reference cli_error.api_endpoint, and neither does any widget on the 12 CLI dashboards.

Endpoints missing from the public catalog, such as /auth/, /envelope/, and events-stats, now report {id} in place of their name. Full URLs outside /api/0/, such as OAuth or GitHub, are left as they are.

Before

cli_error.api_endpoint: /api/0/projects/{org}/{project}/events/{id}/
api_error.endpoint:     organizations/{org}/releases/
span.description:       GET https://sentry.example.com/sentry/api/0/organizations/{org}/issues/
cli_error.api_endpoint: organizations/{org}/not-an-endpoint/my-thing/

After

cli_error.api_endpoint: /api/0/projects/{organization_id_or_slug}/{project_id_or_slug}/events/{event_id}/
api_error.endpoint:     organizations/{organization_id_or_slug}/releases/
span.description:       GET https://sentry.example.com/sentry/api/0/organizations/{organization_id_or_slug}/issues/
cli_error.api_endpoint: organizations/{organization_id_or_slug}/{id}/{id}/

via @betegon.

--

View Session [Sentry]

sentry-junior Bot and others added 2 commits November 13, 2026 07:29
`normalizeEndpoint` only templated paths with a leading slash, so paths
relative to `/api/0/` reached telemetry with raw org and project slugs.
This also normalizes full URLs, drops query strings, and templates short
IDs, monitor slugs, and numeric IDs. The same normalization now applies
to the `api_error` context, the `api_error.endpoint` span attribute of
`sentry api`, and the description and URL attributes (`url`, `url.full`,
`http.url`, `url.path`) of `http.client` spans.

On 0.48.0+, no `api_error.endpoint` value (~6K spans in 14d) and no
`http.client` span description (~1.3M of ~1.4M in 14d carry an org path)
was templated. Some `cli_error.api_endpoint` values still carried raw
slugs, through relative paths or short IDs.

**Before**
```
cli_error.api_endpoint: organizations/my-org/artifactbundle/assemble/
cli_error.api_endpoint: /organizations/{org}/shortids/MY-PROJECT-1A/
api_error.endpoint:     projects/my-org/my-project/issues/?query=is:unresolved
span.description:       GET https://sentry.io/api/0/organizations/my-org/issues/
```

**After**
```
cli_error.api_endpoint: organizations/{org}/artifactbundle/assemble/
cli_error.api_endpoint: /organizations/{org}/shortids/{id}/
api_error.endpoint:     projects/{org}/{project}/issues/
span.description:       GET https://sentry.io/api/0/organizations/{org}/issues/
```

<!-- junior-request-attribution:start -->
via @betegon.
<!-- junior-request-attribution:end -->

<!-- junior-session-footer:start -->
<!-- junior-conversation-id:agent%3Af73d86afcba15ac6080900f12725b985 -->

--

[View
Session](https://junior-prod.sentry.dev/conversations/agent%3Af73d86afcba15ac6080900f12725b985)
[[Sentry]](https://sentry.sentry.io/explore/conversations/agent%3Af73d86afcba15ac6080900f12725b985/?project=4510944073809921)

<!-- junior-session-footer:end -->

---------

Co-Authored-By: bete <28983162+betegon@users.noreply.github.com>
Replace normalizeEndpoint's regex list with the OpenAPI path templates
from @sentry/api, which generate-api-schema now writes to
src/generated/api-path-templates.json so the CLI never loads the full
catalog. The most specific template that matches the most of a path wins,
and segments past it that the templates don't spell out become {id}, so a
path outside the catalog leaks nothing.

Placeholders now use the OpenAPI names, such as
{organization_id_or_slug} and {project_id_or_slug}, which changes
existing cli_error.api_endpoint values.

Co-Authored-By: bete <28983162+betegon@users.noreply.github.com>
@sentry-junior
sentry-junior Bot requested a review from betegon November 13, 2026 07:46
@sentry-junior
sentry-junior Bot added this pull request to stack #2230 November 13, 2026 07:46
@vercel

vercel Bot commented Nov 13, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated
sentry-local Skipped Skipped Nov 13, 2026 7:46am UTC

Request Review

@github-actions github-actions Bot added the risk: medium PR risk score: medium label Nov 13, 2026
[/\/customers\/[^/]+/, "/customers/{org}"],
];
/** The API version prefix every OpenAPI path template starts with. */
const API_PREFIX = "/api/0";

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Should this also come from the @sentry/api package? We can make it happen

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Yes, I think it should. @sentry/api doesn't export the API prefix today, in 0.428.0 on this branch or 1.59.0 on main. Toolkit defines "/api/0" privately in three places: toolkit-core/src/api-client/core.ts, cli/src/commands/api.ts, and now error-reporting.ts. An export from @sentry/api, for example next to buildSentryApiUrl, would let all three share it.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Well then, make it happen. Ideally this would come all the way from the source in getsentry/sentry so we don't drift.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I looked at what it would take to source this from the spec:

  • getsentry/sentry: the OpenAPI spec's servers entry is just https://{region}.sentry.io. /api/0 is spelled out at the start of every path in the spec, so the prefix isn't a separate field there today.
  • getsentry/sentry-api-schema (publishes @sentry/api): the prefix is private there too. It's a const API_PREFIX in lib/api-client/core.ts, plus hard-coded /api/0/ in api-path.ts and api-path-scope.ts.

To avoid drift, sentry-api-schema could derive the prefix from the spec's paths at build time and export it. toolkit's three copies would then import it. Moving /api/0 into servers in getsentry/sentry would also work, but it changes the spec for every client.

This is a change in other repos, so I'm checking the scope with @betegon before I start. I'll leave this thread open until then.

BYK added a commit that referenced this pull request Nov 13, 2026
**Depends on:** `@sentry/api` 2.0.0, which is published
([getsentry/sentry-api-schema#148](getsentry/sentry-api-schema#148)).
This is the last step of the chain sentry-api-schema#148 →
[sentry-docs#20787](getsentry/sentry-docs#20787)
→ [sentry#131065](getsentry/sentry#131065) →
this PR.

Sentry's OpenAPI spec is moving `/api/0` out of every path and into its
server URL. `@sentry/api` 2.0 reads the base path from that URL, exports
it as `API_BASE_PATH`, and makes operation paths relative. This PR drops
toolkit's private `/api/0` copies and uses the export instead, so the
version number lives only in the spec.

Before:
```ts
const API_PREFIX = "/api/0";                          // cli/src/commands/api.ts
this.apiPrefix = `${protocol}://${host}/api/0`;       // mcp-core api-client
path: operation.path,                                 // "/api/0/organizations/" in 1.x
```
After:
```ts
import { API_BASE_PATH } from "@sentry/api/client";
this.apiPrefix = `${protocol}://${host}${API_BASE_PATH}`;
path: `${API_BASE_PATH}${operation.path}`,            // still "/api/0/organizations/"
```

- **CLI endpoint index:** it keeps full request paths, so `sentry
schema` and `sentry api` still show and accept `/api/0/...`.
- **Requests:** CLI raw requests now pass relative URLs to
`prepareRequest`.
- **Unchanged on purpose:** user-facing help text and examples still say
`/api/0`.
- **Breaking:** only the `@sentry/api` 1.x → 2.0.0 bump. CLI and MCP
behavior doesn't change, so no toolkit major is needed.

**#2229 (stacked on #1807):** on 2.0 it needs two changes:
- replace its local `API_PREFIX = "/api/0"` in `error-reporting.ts` with
`API_BASE_PATH`;
- build `api-path-templates.json` from `` `${API_BASE_PATH}${op.path}`
``, because `op.path` is now relative.

**Checks:**
- `pnpm run tsc` and `pnpm run lint` pass.
- mcp-core vitest: 2301 passed.
- The CLI api, schema, sentry-client, error-reporting, issue-links and
script tests: 82 files, 1685 passed.
- `pnpm install --frozen-lockfile` passes with the
`minimumReleaseAgeExclude` entry moved from 1.83.0 to 2.0.0.

<!-- junior-session-footer:start -->
<!-- junior-conversation-id:agent%3A640c5343d03e547097aa9b8a711daaf5 -->

--

[View
Session](https://junior-prod.sentry.dev/conversations/agent%3A640c5343d03e547097aa9b8a711daaf5)
[[Sentry]](https://sentry.sentry.io/explore/conversations/agent%3A640c5343d03e547097aa9b8a711daaf5/?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>

This branch was previously deployed

1 inactive deployment
Preview — 6167d871 Deployed Nov 13, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

risk: medium PR risk score: medium

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants