Conversation
Add initial boilerplate draft for capturing Sentry SDK init options. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Describe that we do not capture SDK config today, and the adjacent signals (event SDK metadata, client reports). Remove the empty Supporting Data section for now. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Option A (preferred): a dedicated, first-class envelope item for SDK options. Option B: expand error events to carry the config. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Propose a language-agnostic, primitives-only payload: sdk (identity + opt-in integration status via `active`), meta, options, integration_options, and a free-form `_other` bucket. Cover callback normalization, generic integration options, reflecting which integrations are actually used, and cross-SDK naming. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Sending: server SDKs (debounced send after init, configurable delay, flush on shutdown), client SDKs (send-on-every-init vs. sampling), and a note that serverless shares client constraints. - Storing: store-every-record vs. dedup-by-release (recommended), with an open question on whether release is a sufficient dedup key. - Add top-level `timestamp` to the envelope; note options are scrubbed server-side (no client-side scrubbing). - Add Drawbacks. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Rename XXXX -> 0162 now that the PR is open, fill in the RFC PR link, and add the index entry to README. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add an "Envelope item type" subsection recommending `sdk_config`, with `sdk_options` and `client_config` as considered alternatives. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Add "Not in scope / Follow-up work" (product usage; removing SDK metadata from events, which needs a backfill from sdk_config). - Cross-SDK naming: recommend leaving option names unspecced (native SDK names as-is) rather than a canonical catalog. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
| ### Option 2 (recommended): Deduplicate and store a single record | ||
|
|
||
| Store only **one** record per configuration and discard the rest. Because configuration is | ||
| essentially constant per release, we need a key to deduplicate by, and we suggest **release**: |
There was a problem hiding this comment.
If we go for release we could still end up with many different init constellations because of options passed in by ENV vars
There was a problem hiding this comment.
updated to propose deduping by release+env+dist+hash!
…n Relay Rework the envelope shape so SDKs send their final/effective `options` as-is plus a flat `options_set_by_user` provenance array, and Relay derives `normalized_options` (a canonical-keyed subset) at ingestion. Add explicit serialization rules (functions -> [Function], integrations -> name), the normalization catalog, and rationale for both decisions (Relay-side normalization; effective options + flat set-by-user array). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…iming; add Open Questions - Rename the per-integration runtime signal `active` -> `applied` (clearer: "took effect at runtime" rather than enabled/disabled). - Reframe server-SDK send timing: debounce is one example default; any settling hook/mechanism is fine as long as final options are captured. - Note the new `sdk_config` envelope item degrades gracefully — older Relay/self-hosted discards the unknown item and processes the rest. - Add an Open Questions section (new-envelope-type infra: rate limits, payload/size limits, data category/quota/billing, outcomes) and reference the release dedup-key question. - Clarify scrubbing: primarily server-side; SDKs MAY scrub known-sensitive values as best-effort, without guaranteeing fully-scrubbed data. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Replace the two parallel name-keyed maps (`sdk.integrations` for status, top-level `integration_options` for options) with a single top-level `integrations` map. Each entry holds the integration's runtime `applied` status plus its configured options nested under an `options` key, so arbitrary user options can never collide with well-known status keys. Also document a known limitation of sending effective options: the user-provided *form* is lost when the SDK coerces a value (notably `integrations` passed as a function -> resolved array). Note this is rare (essentially `integrations` and `stackParser` in JS) and can be layered on later via a sparse `options_user_provided` block without reworking the shape. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ns hash Replace the two-strategy + hybrid dedup framing with a single rule: deduplicate by `release` + `environment` + `dist` plus an optional, SDK-provided options hash that is stable across instances sharing a config. When present, the server folds the hash into the dedup key (capturing within-release variation); when absent, dedup falls back to the composite key. The hash is SDK-side by design (a robust server-side canonical hash isn't feasible) and, if used, MUST be stamped on every event — as an attribute on spans/logs and a context field on error/transaction events — so events correlate exactly to their config. Correlation now falls out of the key: bucket-level without a hash, exact with one. Update Open Questions and the Drawbacks bullets to match. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
@mydea I think we can greatly reduce the length of this RFC and make it a lot easier for folks to read over and quickly understand. I opened a concrete proposal against this PR branch in #163. The shortened version should preserve the existing RFC as written without applying any code review suggestions from myself or others |
I do not see the need to make this officially optional. But SDKs can implement this in a fidelity that makes sense for them. E.g. if certain information/data is not reasonably accessible, we can omit things as it makes sense. At this stage, we should mostly get a feeling for which things specifically may not be feasible in certain SDKs and then make those parts of the spec optional/extensible. |
As long as the spec does not end up forcing us to redesign SDKs to be able to send this data, beyond just adding the additional envelope types and collecting the data that is easily collected, I am satisfied with it 👍 |
|
|
||
| | Option | Pros | Cons | | ||
| | ----------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- | | ||
| | **I: Send on every `init()`** | Simple, same as server SDKs; every instance is represented | Much more data to ingest; an extra request per `init()` | |
There was a problem hiding this comment.
With regards to the: "Much more data"
Do we have any idea about the distribution of "telemetry per sdk instance" (or simpler how much envelopes does an sdk send on avg)? Are we assuming that there is a long tail of SDKs that are initiated but never actually emit anything afterwards?
There was a problem hiding this comment.
this is just about client sdks - where way more instances are spawned, thus way more Sentry.init() is called than on a server which may only be started once with the given settings :)
| | Option | Pros | Cons | | ||
| | ----------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- | | ||
| | **I: Send on every `init()`** | Simple, same as server SDKs; every instance is represented | Much more data to ingest; an extra request per `init()` | | ||
| | **II: Sample** a fraction of `init()` calls (e.g. 1%), assuming configuration is essentially constant per release | Much less overhead | Low-traffic releases and rare configurations may be missed; more configuration surface; harder to reason about and test | |
There was a problem hiding this comment.
Could we sample on the client side based on the hash calculated? So the SDK would only send the config if the hash is different than the last sent item.
I realize this means we will need to make use of storage like localStorage which has no precedent, and probably something we never do, but this is just an idea.
There was a problem hiding this comment.
yeah we'd need to store this in local storage which we can't by default, and why would you opt-in to that 😅 after talking to ingest folks etc. it seems that it's not really a concern to send this all the time, so this is the simplest for now IMHO.
| - **Overhead:** a new, potentially high-volume item (every `init()` for client and serverless | ||
| traffic), a new storage model, and new server-side processing. |
There was a problem hiding this comment.
We have a client-side precedent for high volume events in the browserSessionIntegration but the envelope is a few bytes compared to the sdk config.
|
|
||
| | Option | Pros | Cons | | ||
| | ----------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- | | ||
| | **I: Send on every `init()`** | Simple, same as server SDKs; every instance is represented | Much more data to ingest; an extra request per `init()` | |
There was a problem hiding this comment.
Probably an implementation detail but wanted to bring it up.
For SDKs with performance concerns like the browser, the sending should be deferred until the timing is appropriate, for browsers we could send on whenIdleOrHidden like with session envelopes.
On the browser, sending immediately on init could affect LCP and other web metrics.
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 1139b7e. Configure here.
| "sentry.sdk_config.version": { "type": "integer", "value": 1 }, | ||
| "sentry.sdk_config.hash": { "type": "string", "value": "9f2c1a7e" }, | ||
|
|
||
| "sentry.sdk_config.option.dsn": { "type": "string", "value": "https://<public-key>@o0.ingest.sentry.io/0" }, |
There was a problem hiding this comment.
Mh these prefixes make sense but it's quite redundant in the scope of a sdk config.
There was a problem hiding this comment.
yeah, good point. I guess it depends on how we want to approach this, should these be "regular conventions" or type-specific conventions 🤔
There was a problem hiding this comment.
I think it is easiest to still make the proper, global-defined semantic attributes. it's a bit verbose here but at least clear, we can document in conventions that these are internal and only to be used for this envelope type.
There was a problem hiding this comment.
It also fits in better with other attributes like process.runtime.name if everything is from/in the conventions namespace.

WIP — draft RFC, still in progress.
Today we have no visibility into which options a given Sentry SDK instance was configured with. This RFC proposes a mechanism for SDKs to report the configuration they were initialized with (the arguments passed to
Sentry.init(), plus relevant derived/effective values) to Sentry via a new, dedicatedsdk_configenvelope item, so it can be stored, surfaced, and acted upon — decoupled from whether the instance ever sends an event.Knowing the configured options unlocks a range of use cases:
init()setups to suggest improvements or warn about potentially confusing behavior given the settings.Sentry.init()s producing data into a project, understand their sources, and see the filtering/sampling/config that affects them, including changes over time.The RFC focuses on the capture and transport mechanism — the envelope shape (which options we capture, how non-serializable values and integrations are normalized, cross-SDK naming, Relay-side normalization), when SDKs send it, and how it is deduplicated and stored server-side. The downstream use cases above are motivation and future work.
Linear project: https://linear.app/getsentry/project/capture-sdk-options-js-08e8a89c71c9/overview
Rendered RFC