Skip to content

fix: defer UserInteractionInstrumentation enable() past field init to fix _eventNames TypeError #SUPERLOG - #416

Open
superlog-app[bot] wants to merge 1 commit into
mainfrom
superlog/fix-user-interaction-eventnames-init-chalky-lynx
Open

fix: defer UserInteractionInstrumentation enable() past field init to fix _eventNames TypeError #SUPERLOG#416
superlog-app[bot] wants to merge 1 commit into
mainfrom
superlog/fix-user-interaction-eventnames-init-chalky-lynx

Conversation

@superlog-app

@superlog-app superlog-app Bot commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

Summary

Every user click on the production web app was throwing TypeError: this._eventNames is undefined inside the Zone.js runTask patch, generating 207+ noisy browser.exception spans in ~46 seconds. The global window.error handler added in #414 surfaced this pre-existing silent failure.

Root Cause

UserInteractionInstrumentation@0.59.0 has a class field initialization ordering bug when bundled by Vite/esbuild. InstrumentationBase.constructor calls this.enable() inside super(), before the derived class's own field initializers run. Vite transforms _eventNames; (class field, no initializer) into an explicit I(this,"_eventNames") statement placed after super() but before the constructor body assignment this._eventNames = new Set(['click']).

Confirmed in the production bundle (index-CS7X72oT.js:15:79301):

// Inside sN (UserInteractionInstrumentation) constructor:
super(eN,sS,i);          // ← enable() fires here, patches Zone.js with plugin=this
// …
I(this,"_eventNames");   // ← _eventNames = undefined  ← WRONG STATE
this._eventNames=new Set();  // ← set correctly, but too late for async Zone tasks

When Zone.js tasks fire asynchronously (React dynamic-import Promise callbacks, click events) between those two lines, _allowEventName calls this._eventNames.has(eventName) and throws. The error propagates to window.onerror, the new handler captures it as a browser.exception span.

Fix

Pass { enabled: false } to the constructor so enable() is not called during super(). Call enable() explicitly after the constructor returns, at which point _eventNames = new Set(['click']) is guaranteed. Zone.js is then patched with the correct state.

registerInstrumentations calls c.getConfig().enabled || c.enable() — since enabled: false, it would call enable() again, but _isEnabled is already true from our explicit call, so it returns immediately with no double-patching.

Impact

Stops 207+ false-positive browser.exception spans per session on the / route. Click-to-fetch trace correlation continues to work correctly after this fix.


Fixes incident f47626e5-9d9d-4ef7-aed5-cdc22414fce1 (chalky-lynx).


Was this PR helpful? Leave feedback — goes straight to the Superlog team.


Summary by cubic

Defer enabling of UserInteractionInstrumentation until after its fields are initialized to prevent _eventNames TypeError. This stops noisy browser.exception spans and keeps click instrumentation working.

  • Bug Fixes
    • Instantiate UserInteractionInstrumentation with { enabled: false } and call .enable() after construction, avoiding InstrumentationBase calling enable() in super().
    • Ensures _eventNames is set before Zone.js patches run, fixing the Vite/esbuild class field order issue and removing 200+ exception spans per session.

Written for commit ad67b66. Summary will update on new commits.

Review in cubic

… fix _eventNames TypeError #SUPERLOG

Delivery-Id: 1873234cdfe0ad22126e363649481acbe9904f45693c6c4a4c1a9ac3cecae0eb
Delivery-Base: main

@superlog-app superlog-app Bot left a comment

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.

Observability review

All clear — no observability concerns found.

Keep shipping :shipit:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant