fix(analytics-core): drop events with an empty event type before upload - #1920
Open
Mercy811 wants to merge 2 commits into
Open
fix(analytics-core): drop events with an empty event type before upload#1920Mercy811 wants to merge 2 commits into
Mercy811 wants to merge 2 commits into
Conversation
Events with a blank event type are rejected by the event server with a 400, which also forces the rest of their upload batch to be retried. 99.6% of dropped Browser SDK events have a blank event name. Validate client-side in AmplitudeCore.process() instead: events whose event_type is empty, whitespace-only, or not a string are dropped with a warning log before reaching the timeline or any destination. Mirrors the same check added to the Kotlin SDK in amplitude/Amplitude-Kotlin#444. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Contributor
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: a72764b815
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Contributor
Author
|
bugbot run |
size-limit report 📦
|
There was a problem hiding this comment.
✅ Bugbot reviewed your changes and found no new issues!
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit a72764b. Configure here.
crleona
approved these changes
Jul 31, 2026
daniel-graham-amplitude
requested changes
Jul 31, 2026
A dropped event is something customers need surfaced quickly so they can correct the call site, and LogLevel.Error (1) is below Warn (2), so error() is visible at every log level except None. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
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.
Summary
SDKW-50 — 99.6% of dropped Browser SDK events have a blank event name and get 400d by the event server (Slack thread). Beyond being pointless traffic, a blank event type in a batch forces the rest of that batch to be retried.
This adds the same client-side validation the Kotlin SDK just picked up in amplitude/Amplitude-Kotlin#444: drop the event before upload, with a log line calling it out.
The log is at error level rather than warn (per review): a dropped event is something customers want surfaced quickly, and since
LogLevel.Erroris1whileWarnis2,error()is visible at every log level exceptNone.Where:
AmplitudeCore.process()(packages/analytics-core/src/core-client.ts), right after the existingoptOutcheck — the direct analogue of Kotlin'sAmplitude.process(). This is the single funnel into the timeline: bothdispatch()anddispatchWithCallback()route through it, so the check coverstrack/logEvent,identify,groupIdentify,setGroup, andrevenuefor every SDK built on core (browser, node, react-native).What counts as empty:
event_typethat is empty, whitespace-only, or not a string. Kotlin'sisBlank()covers empty + whitespace; thetypeofguard is the JS-specific part —track({})ortrack(undefined)reachesprocess()withevent_type === undefined, sincecreateTrackEventpasses object input straight through.Dropped events return
buildResult(event, 0, EMPTY_EVENT_TYPE_MESSAGE), matching howoptOutandCLIENT_NOT_INITIALIZEDalready report skipped events, so callers awaitingtrack()get a result rather than a hang, and the promise message matches the logged error.Not covered (deliberate, matches Kotlin): the destination plugin's
setup()restores unsent events from storage and callsexecute()directly, bypassingprocess(). Blank events persisted by older SDK versions will still be uploaded once — but a 400 lists them ineventsWithInvalidFields, sohandleInvalidResponsedrops them instead of retrying forever. Not worth a second gate.Testing
New tests in
packages/analytics-core/test/core-client.test.tsunderdescribe('process'), mirroring the Kotlin test's shape (dropped event never reaches the pipeline; a valid event still flows):test.eachover empty / whitespace-only / missingevent_type— assertstimeline.pushis never called, the result isEMPTY_EVENT_TYPE_MESSAGEwith code 0, and exactly one error is logged.event_typestill reachestimeline.pushwith nothing logged toerror.Verified locally:
analytics-core: 873 tests pass, 100% statements/branches/functions/lines (the package's enforced threshold).analytics-browser: 499 tests pass, 100% coverage.pnpm test: all 29 projects pass.pnpm lint(0 errors) andpnpm typecheckclean onanalytics-core.Checklist
track()promise resolves with code 0 instead of 400.🤖 Generated with Claude Code
Note
Low Risk
Narrow validation in the shared event funnel; no API or auth changes, though callers now get a local code-0 skip instead of a failed upload for blank event names.
Overview
Client-side guard in
AmplitudeCore.process()drops events whoseevent_typeis missing, not a string, or only whitespace—right after the existingoptOutcheck and beforetimeline.push. That blocks doomed uploads that 400 the event server and can force batch retries.Dropped events follow the same skip pattern as opt-out:
buildResultwith code0, messageEMPTY_EVENT_TYPE_MESSAGE, and a warning on the logger. Valid event types still go through the pipeline unchanged.Tests cover empty, whitespace-only, and undefined
event_type(nopush, correct result/warn) plus a happy path that still callspush.Reviewed by Cursor Bugbot for commit a72764b. Configure here.