Skip to content

fix(core): use typeof check in isEvent to prevent ReferenceError in non-browser runtimes#3269

Open
leifarriens wants to merge 1 commit intoPostHog:mainfrom
leifarriens:fix/core-is-event-reference-error
Open

fix(core): use typeof check in isEvent to prevent ReferenceError in non-browser runtimes#3269
leifarriens wants to merge 1 commit intoPostHog:mainfrom
leifarriens:fix/core-is-event-reference-error

Conversation

@leifarriens
Copy link

Problem

Error tracking in posthog-react-native is non-functional. When initializing PostHog with error tracking autocapture enabled:

new PostHog("phc_", {
  errorTracking: {
    autocapture: {
      console: [],
      uncaughtExceptions: true,
      unhandledRejections: true,
    },
  },
});

No $exception events are ever sent. The isEvent() utility in @posthog/core directly references the Event global, which doesn't exist in React Native's Hermes runtime. The resulting error surfaces as:

ERROR [PostHog] [ErrorTracking] An error occurred while capturing an $exception event: [ReferenceError: Property 'Event' doesn't exist]

Changes

  • Use typeof Event !== 'undefined' instead of !isUndefined(Event) in isEvent() to safely handle runtimes where Event doesn't exist.
  • Added tests for isEvent().

Release info Sub-libraries affected

Libraries affected

  • All of them
  • posthog-js (web)
  • posthog-js-lite (web lite)
  • posthog-node
  • posthog-react-native
  • @posthog/react
  • @posthog/ai
  • @posthog/convex
  • @posthog/next
  • @posthog/nextjs-config
  • @posthog/nuxt
  • @posthog/rollup-plugin
  • @posthog/webpack-plugin
  • @posthog/types

Checklist

  • Tests for new code
  • Accounted for the impact of any changes across different platforms
  • Accounted for backwards compatibility of any changes (no breaking changes!)
  • Took care not to unnecessarily increase the bundle size

If releasing new changes

  • Ran pnpm changeset to generate a changeset file
  • Added the "release" label to the PR to indicate we're publishing new versions for the affected packages

@vercel
Copy link

vercel bot commented Mar 21, 2026

@leifarriens is attempting to deploy a commit to the PostHog Team on Vercel.

A member of the Team first needs to authorize it.

@greptile-apps
Copy link
Contributor

greptile-apps bot commented Mar 21, 2026

Prompt To Fix All With AI
This is a comment left during a code review.
Path: packages/core/src/utils/type-utils.spec.ts
Line: 34-38

Comment:
**Test can silently pass without asserting**

The `if (typeof Event !== 'undefined')` guard means this test will pass vacuously in any environment where `Event` is not defined (e.g., a Node.js test runner without jsdom). No assertion is ever executed and the test reports success while actually verifying nothing.

Consider using `it.skipIf` (Vitest) or a similar mechanism to make the skip explicit and visible:

```
it.skipIf(typeof Event === 'undefined')('returns true for Event instances when Event global exists', () => {
  expect(isEvent(new Event('test'))).toBe(true)
})
```

Or, if the test environment is always jsdom (where `Event` is defined), remove the guard entirely since it's never needed.

How can I resolve this? If you propose a fix, please make it concise.

---

This is a comment left during a code review.
Path: packages/core/src/utils/type-utils.spec.ts
Line: 40-46

Comment:
**Prefer parameterised test**

The existing tests in this file use `it.each` for parameterised cases (see `isNumber` and `isPositiveNumber`). This test checks multiple non-Event values individually — it should follow the same pattern for consistency:

```suggestion
    it.each([
      [new Error('test'), false],
      ['string', false],
      [{}, false],
      [null, false],
      [undefined, false],
    ])('isEvent(%p) returns %p', (value, expected) => {
      expect(isEvent(value)).toBe(expected)
    })
```

How can I resolve this? If you propose a fix, please make it concise.

Last reviewed commit: "fix(core): use typeo..."

Comment on lines +34 to +38
it('returns true for Event instances when Event global exists', () => {
if (typeof Event !== 'undefined') {
expect(isEvent(new Event('test'))).toBe(true)
}
})
Copy link
Contributor

Choose a reason for hiding this comment

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

P2 Test can silently pass without asserting

The if (typeof Event !== 'undefined') guard means this test will pass vacuously in any environment where Event is not defined (e.g., a Node.js test runner without jsdom). No assertion is ever executed and the test reports success while actually verifying nothing.

Consider using it.skipIf (Vitest) or a similar mechanism to make the skip explicit and visible:

it.skipIf(typeof Event === 'undefined')('returns true for Event instances when Event global exists', () => {
  expect(isEvent(new Event('test'))).toBe(true)
})

Or, if the test environment is always jsdom (where Event is defined), remove the guard entirely since it's never needed.

Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/core/src/utils/type-utils.spec.ts
Line: 34-38

Comment:
**Test can silently pass without asserting**

The `if (typeof Event !== 'undefined')` guard means this test will pass vacuously in any environment where `Event` is not defined (e.g., a Node.js test runner without jsdom). No assertion is ever executed and the test reports success while actually verifying nothing.

Consider using `it.skipIf` (Vitest) or a similar mechanism to make the skip explicit and visible:

```
it.skipIf(typeof Event === 'undefined')('returns true for Event instances when Event global exists', () => {
  expect(isEvent(new Event('test'))).toBe(true)
})
```

Or, if the test environment is always jsdom (where `Event` is defined), remove the guard entirely since it's never needed.

How can I resolve this? If you propose a fix, please make it concise.

Comment on lines +40 to +46
it('returns false for non-Event values', () => {
expect(isEvent(new Error('test'))).toBe(false)
expect(isEvent('string')).toBe(false)
expect(isEvent({})).toBe(false)
expect(isEvent(null)).toBe(false)
expect(isEvent(undefined)).toBe(false)
})
Copy link
Contributor

Choose a reason for hiding this comment

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

P2 Prefer parameterised test

The existing tests in this file use it.each for parameterised cases (see isNumber and isPositiveNumber). This test checks multiple non-Event values individually — it should follow the same pattern for consistency:

Suggested change
it('returns false for non-Event values', () => {
expect(isEvent(new Error('test'))).toBe(false)
expect(isEvent('string')).toBe(false)
expect(isEvent({})).toBe(false)
expect(isEvent(null)).toBe(false)
expect(isEvent(undefined)).toBe(false)
})
it.each([
[new Error('test'), false],
['string', false],
[{}, false],
[null, false],
[undefined, false],
])('isEvent(%p) returns %p', (value, expected) => {
expect(isEvent(value)).toBe(expected)
})
Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/core/src/utils/type-utils.spec.ts
Line: 40-46

Comment:
**Prefer parameterised test**

The existing tests in this file use `it.each` for parameterised cases (see `isNumber` and `isPositiveNumber`). This test checks multiple non-Event values individually — it should follow the same pattern for consistency:

```suggestion
    it.each([
      [new Error('test'), false],
      ['string', false],
      [{}, false],
      [null, false],
      [undefined, false],
    ])('isEvent(%p) returns %p', (value, expected) => {
      expect(isEvent(value)).toBe(expected)
    })
```

How can I resolve this? If you propose a fix, please make it concise.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

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