Skip to content

fix(useScriptTag): handle the auto-load rejection - #206

Merged
childrentime merged 1 commit into
childrentime:mainfrom
Faithfinder:pr/handle-script-load-rejection
Jul 28, 2026
Merged

fix(useScriptTag): handle the auto-load rejection#206
childrentime merged 1 commit into
childrentime:mainfrom
Faithfinder:pr/handle-script-load-rejection

Conversation

@Faithfinder

Copy link
Copy Markdown
Contributor

Problem

The immediate auto-load calls load() as a bare statement:

useMount(() => {
  if (immediate && !manual) {
    load()
  }
})

Nothing is attached to the promise it returns. When the script fails — blocked by an ad blocker, offline, 404 — the error listener rejects that promise, and with no handler the rejection reaches window.onunhandledrejection, where error trackers report it. Setting status to 'error' does not mark the promise handled; setStatus and reject are independent.

Any useScriptTag pointing at analytics, a chat widget, or a third-party SDK hits this for every user running a blocklist. In our app it was the single highest-volume Sentry issue — 5231 events across 405 users, from three scripts.

Fix

load().catch(noop) in the auto-load path.

status === 'error' stays the reporting channel for a load nobody awaited. Callers that hold the promise themselves are unaffected: load() memoizes into _promise.current, so an explicit load() returns the same promise and still rejects for them.

Tests

Two added to useScriptTag/index.spec.ts — one per direction. Both fail on main, both pass with the fix.

One note for review: jest intercepts unhandled rejections at the VM level, so neither process.on('unhandledRejection') nor the jsdom unhandledrejection event can observe them from inside a test — I tried both. Jest's own reporter does fail the suite on one, which is what makes the first test a regression guard rather than just a status assertion. There's a comment above it saying so, since it isn't obvious from the assertion alone.

pnpm lint and the full @reactuses/core suite (307 tests) pass.


Written with Claude Code; I've read and understood every line, per .claude/ai-policy.md.

The `immediate` auto-load calls `load()` as a bare statement inside
useMount, so nothing is attached to the promise it returns. When the
script fails - blocked by an ad blocker, offline, 404 - the DOM error
listener rejects that promise and the rejection reaches
window.onunhandledrejection, where error trackers report it. Setting
`status` to 'error' does not mark the promise handled.

Attach `.catch(noop)` to the auto-load. Callers that hold the promise
themselves still see the rejection, since `load()` memoizes and returns
the same promise.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@Faithfinder
Faithfinder force-pushed the pr/handle-script-load-rejection branch from 64f5f51 to 0579fcf Compare July 28, 2026 14:33

@childrentime childrentime left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Verified locally: both new tests fail on main and pass with the fix; pnpm lint and the full @reactuses/core suite (307 tests) are green.

load().catch(noop) attaches the handler to the memoized _promise.current, so the auto-load no longer surfaces as an unhandled rejection while an explicit load() still rejects for callers that hold it. status === 'error' remains the reporting channel. Minimal and correct — thanks!

@childrentime
childrentime merged commit fdd9384 into childrentime:main Jul 28, 2026
4 checks passed
childrentime added a commit that referenced this pull request Jul 28, 2026
Fixes
- useScriptTag no longer surfaces an unhandled promise rejection when the
  `immediate` auto-load fails (ad blocker, offline, 404). The auto-load now
  attaches a no-op catch; `status === 'error'` stays the reporting channel and
  an explicit `load()` still rejects for callers holding the promise. Closes #206.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@childrentime

Copy link
Copy Markdown
Owner

Released in @reactuses/core@6.4.1 — thanks for the fix and the thorough write-up!

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