Skip to content

fix(analytics-browser): guard snippet bootstrap against undefined _iq and _q#1763

Open
Mercy811 wants to merge 2 commits into
mainfrom
claude/kind-raman-457f55
Open

fix(analytics-browser): guard snippet bootstrap against undefined _iq and _q#1763
Mercy811 wants to merge 2 commits into
mainfrom
claude/kind-raman-457f55

Conversation

@Mercy811
Copy link
Copy Markdown
Contributor

@Mercy811 Mercy811 commented May 16, 2026

Summary

Two TypeErrors in the snippet bootstrap. Combined: 7,612 of 15,226 real SDK uncaught errors (~50%) over 7 days, surfaced via the SDK Diagnostics – Browser SDK dashboard.

What's going wrong

When the bundle starts up it checks window.amplitude.invoked to decide "the snippet ran, the queues _q and _iq are ready — drain them." But invoked = true can be set without _q / _iq ever existing. When that happens, the bootstrap crashes.

This isn't really a snippet template bug — the template always seeds both queues. The bundle just trusts the global state too much. Most likely sources of the bad state:

  1. Browser extensions / tracking blockers / in-app webview wrappers that stub window.amplitude = { invoked: true, track: noop } to neuter analytics. They don't know _iq is supposed to exist. Matches the heavy skew toward Instagram / Mobile Safari in-app browsers in the logs.
  2. Two Amplitude integrations on the same page — e.g. an older internal wrapper that sets invoked: true and _q but predates _iq, plus a newer <script src=...> bundle loaded directly. The wrapper wins the global.

Fix: have the bootstrap normalize _q and _iq itself instead of trusting them.

The bugs

1. Object.keys(amplitude._iq) at snippet-index.ts:52 — 6,167 events / 7d

Object.keys(_iq) || [] doesn't help: Object.keys(undefined) throws before || is ever evaluated.

Datadog logs

{
  "event_name": "sdk.error.uncaught",
  "app_id": "<redacted>",
  "sdk_tags": {
    "library": "amplitude-ts-sdk-script/2.39.0",
    "os_name": "Mobile Safari",
    "device_model": "iOS"
  },
  "event_properties": {
    "error_name": "TypeError",
    "message": "TypeError: undefined is not an object (evaluating 'Object.keys(e.amplitude._iq)')",
    "stack_unminified": [
      "at GlobalScope (packages/analytics-browser/src/snippet-index.ts:52:38)"
    ]
  }
}

2. queue.length at snippet-helper.ts:29 — 1,445 events / 7d

When iterating named instances, instance._q can be undefined for the same reason. convertProxyObjectToRealObject now returns early when queue is falsy.

Datadog logs

{
  "event_name": "sdk.error.uncaught",
  "app_id": "<redacted>",
  "sdk_tags": {
    "library": "amplitude-ts-sdk-script/2.39.0",
    "os_name": "Instagram",
    "device_model": "Android"
  },
  "event_properties": {
    "error_name": "TypeError",
    "message": "Cannot read properties of undefined (reading 'length')",
    "stack_unminified": [
      "at length (packages/analytics-browser/src/utils/snippet-helper.ts:29:28)",
      "at convertProxyObjectToRealObject (packages/analytics-browser/src/utils/snippet-helper.ts:21:2)",
      "at runQueuedFunctions (packages/analytics-browser/src/snippet-index.ts:58:6)"
    ]
  }
}

Customer impact

Each crash surfaces an uncaught TypeError in the customer's browser console and any RUM / error-monitoring they run. The main amplitude object IS set up before the crash, so direct amplitude.track(...) calls after the snippet still work — but pre-load queued events on named instances are lost. After the fix, bootstrap completes cleanly.

Changes

Test plan

  • pnpm --filter @amplitude/analytics-browser test — 493 pass (was 492, +1 new test)
  • pnpm --filter @amplitude/analytics-browser lint — clean (pre-existing warnings only)
  • Monitor dashboard after release to confirm the volume drops

🤖 Generated with Claude Code

… and _q

When window.amplitude (or window.amplitudeGTM) is pre-existing with
invoked=true but missing _iq/_q, Object.keys(undefined) and queue.length
both throw uncaught TypeErrors during SDK init. Together these account
for ~70% of uncaught SDK errors in the planning section of the diagnostics
dashboard.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: dc11e22e7b

ℹ️ 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".

Comment thread packages/analytics-browser/src/gtm-snippet-index.ts Outdated
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR hardens the @amplitude/analytics-browser snippet and GTM-snippet bootstrap paths against cases where a pre-existing global amplitude/amplitudeGTM object is marked invoked=true but is missing the _iq and/or _q queues, preventing uncaught TypeErrors during initialization.

Changes:

  • Guard Object.keys(...) calls in snippet bootstrap to safely handle missing _iq.
  • Make convertProxyObjectToRealObject no-op when its queue argument is undefined, and add a regression test for that case.
  • Apply the same _iq guard to the GTM snippet bootstrap.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.

File Description
packages/analytics-browser/src/snippet-index.ts Avoids Object.keys(undefined) when _iq is missing during snippet bootstrap.
packages/analytics-browser/src/gtm-snippet-index.ts Mirrors the snippet bootstrap guard for GTM (amplitudeGTM).
packages/analytics-browser/src/utils/snippet-helper.ts Adds a runtime early-return when the queued-call array is undefined.
packages/analytics-browser/test/utils/snippet-helper.test.ts Adds coverage for the undefined-queue behavior.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread packages/analytics-browser/src/utils/snippet-helper.ts Outdated
Comment thread packages/analytics-browser/test/utils/snippet-helper.test.ts Outdated
…guards

- Normalize `_q` and `_iq` on the pre-existing global so downstream code
  (e.g. the GTM wrapper that reads `amplitude._iq[name]` directly) doesn't
  crash after this bootstrap recovers. Per Codex review on PR #1763.
- Widen `runQueuedFunctions` and `convertProxyObjectToRealObject` to accept
  `QueueProxy | undefined`, matching the runtime behavior added in this PR,
  and drop the unsafe cast in the regression test. Per Copilot review.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@Mercy811
Copy link
Copy Markdown
Contributor Author

@codex

@chatgpt-codex-connector
Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Nice work!

ℹ️ 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".

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated no new comments.

@Mercy811
Copy link
Copy Markdown
Contributor Author

bugbot run

Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

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

✅ 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 c7f45fe. Configure here.

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.

2 participants