Report OpenPanel delivery failures instead of dropping them - #34
Merged
Merged
Conversation
OpenPanel's SDKs treat HTTP 401 as a non-error: their shared `Api.post` returns `null` without throwing, retrying or logging. A wrong client ID, a rotated secret or a browser origin the project does not allow therefore stopped analytics silently, with nothing for an application to catch or a log to show. Both SDKs send through the `api` instance they expose publicly, so the providers replace its `fetch` with an equivalent implementation that reports the response status first. The replacement mirrors the SDK transport: 200 and 202 are the only success statuses, 401 is never retried, other failures back off exponentially, and headers, body and request options are built the same way. Delivery still resolves rather than throwing, so reporting a failure cannot turn a tracked event into an application error. Both providers accept `onDeliveryFailure`; without a handler the failure is logged, because the previous behaviour was no signal at all. The reported failure carries the reason, status, attempt count, OpenPanel envelope type and ingestion URL, never event properties. When a client does not expose the expected transport the SDK keeps delivering events unchanged and the skipped instrumentation is only mentioned in debug logs. Claude-Session: https://claude.ai/code/session_01MmqjXsp5iX3Upj27YiGiw1
Contributor
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
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.
The problem
OpenPanel's SDKs treat HTTP 401 as a non-error. Their shared
Api.postdoes:No throw, no retry, no log. A wrong client ID, a rotated secret, or a browser origin the project does not allow therefore stops analytics silently — nothing for an application to catch, nothing in the logs, and a dashboard that simply goes quiet. Found while wiring up a real project: server events were landing, browser events were 401ing from
localhost, and the only way to see it was to read the response status off the wire.Every other failure the SDK at least
console.errors after its retries. 401 is the one that disappears.The change
Both SDKs send through the
apiinstance they expose publicly, so the providers replace itsfetchwith an equivalent implementation that reports the response status before returning the value the SDK expects.reasonisunauthorized,server_error(still rejected once retries are exhausted) ornetwork_error(never produced a response).Why
client.apiis the seamapiis public in the shipped.d.ts; theApiclass itself is not exported, soopenPanelApiOfmatches it structurally — one cast, in one place, behind runtime guards onfetch,baseUrlandheaders. If a future SDK changes that shape,instrumentOpenPanelDeliveryreturnsfalse, the SDK keeps delivering events unchanged, and only a debug log mentions it. The mocked-SDK tests already exercise that path.The replacement mirrors the SDK transport exactly, so nothing else changes:
maxRetries/initialRetryDelayread off the instance....optionsis spread last, sosend()'skeepalive: falsefor replay still wins.addHeaderis picked up, and headers resolving tonullare dropped.Tests
test/openpanel-delivery.test.tscovers the unit behaviour (no retry on 401, retry-then-report on 5xx, network failures without an invented status, accepted responses returned untouched, the exact request shape, the unrecognized-transport guard, default logging, throwing handlers). Two of them go through the real SDKs with a stubbedfetch— server here, browser inopenpanel-client-provider.web-sdk.test.ts— so an upstream shape change fails CI rather than silently disabling reporting.Locally green:
typecheck,lint,test(373),build,verify:package, docsvalidate --strictand docs build.Notes
@openpanel/sdk'spost(). This is the consumer-side mitigation.buildEventPropertiesspreads event properties first and then writespage,device,utm,category,sessionId,profileIdand the__*keys over them, so an event declaring one of those names loses its value silently. Different blast radius, and it turns out the pattern is repo-wide rather than OpenPanel-specific — filed as Event properties are silently overwritten by provider context keys #35.https://claude.ai/code/session_01MmqjXsp5iX3Upj27YiGiw1