Is there an existing issue for this?
How do you use Sentry?
Sentry Saas (sentry.io)
Which SDK are you using?
@sentry/angular
SDK Version
10.75.0
Framework Version
Angular: 22.1.7
Link to Sentry event
https://remotemdr-9a8bcf225.sentry.io/issues/5748192625/events/9e7fd03395c041b7b3f8721468a6b072/?project=4505866220732416
Reproduction Example/SDK Setup
We do not currently have a deterministic public reproduction. The issue has been observed across multiple production Replays in Safari on macOS.
The relevant SDK setup is equivalent to:
Sentry.init({
integrations: [Sentry.browserTracingIntegration()],
replaysSessionSampleRate: 0,
replaysOnErrorSampleRate: 1.0,
});
Sentry.addIntegration(
Sentry.replayIntegration({
maskAllText: true,
maskAllInputs: true,
blockAllMedia: true,
beforeErrorSampling: (event) => {
if (!event.message) {
return true;
}
return !ignoredMessages.some((message) =>
event.message?.includes(message),
);
},
}),
);
The Replay integration is added after the Angular application has bootstrapped. Additional block, unblock, and unmask selectors have been omitted because they only target ordinary application elements.
The application uses Angular 22.1.7 without zone.js.
Steps to Reproduce
This is not yet deterministically reproducible outside production. The following sequence describes the behavior observed in affected Replays:
- Configure Replay for error-triggered buffering with
replaysSessionSampleRate: 0 and replaysOnErrorSampleRate: 1.0.
- Open the Angular SPA in desktop Safari on macOS.
- Navigate within the SPA and interact with a page containing normal dynamic DOM activity.
- Keep the page open beyond the first 60-second Replay checkout interval.
- Around that boundary, observe opaque
unhandledrejection events in Sentry.
- Continue using the application and navigate to another SPA route.
- Open the resulting Replay.
- Compare the Replay’s current URL and activity timeline with its rendered viewport.
In a representative Replay:
- The first opaque rejection occurred approximately 61.8 seconds after Replay began.
- Another
CustomEvent rejection occurred approximately 60.1 seconds later.
- Subsequent navigation, network, console, WebSocket, media-session, and application diagnostic activity continued.
- The rendered Replay viewport remained permanently stuck on the earlier page.
Most of the affected Replays we manually reviewed show the same frozen-visual-state pattern.
Expected Result
A failure during a periodic Replay checkout should be contained within Replay.
Specifically:
- No Replay-internal failure should escape as a global unhandled promise rejection.
- A failed full snapshot should not leave DOM mutation recording permanently locked or disabled.
- Replay should recover, retry, or stop recording cleanly.
- Subsequent DOM changes and SPA navigation should remain visually consistent with the Replay URL and activity timeline.
- The originating exception and operation should be retained with an actionable stack.
- One internal failure should not create multiple opaque Sentry issue groups.
Actual Result
Around the point where the Replay visual state stops updating, Sentry captures:
CustomEvent: Event CustomEvent (type=unhandledrejection) captured as promise rejection
and:
UnhandledRejection: Object captured as promise rejection with keys: [object has no keys]
A correlated error appears shortly afterward as a sentry.event breadcrumb:
TypeError: Cannot call a class as a function
The originating TypeError does not have a useful stack. The top-level events contain only the global unhandledrejection capture path.
After the failure:
- The application continues operating normally.
- The Replay URL changes when the user navigates.
- Network, console, WebSocket, media-session, and application diagnostic events continue to be recorded.
- The rendered Replay viewport remains permanently stuck on an earlier page.
- No corresponding user-facing crash, disconnection, or navigation failure has been identified.
We reviewed additional Replays belonging to the same Sentry issue. Most of the reviewed Replays show this same mismatch between continuing application activity and a stale visual frame.
Additional Context
Observed scope
All sampled occurrences in this issue group were from Safari on macOS.
One representative affected Replay used Safari 26.6.2. This does not mean the issue reproduces on every Safari installation or every installation of Safari 26.6.2. The exact browser, DOM, and timing conditions required to trigger the failure remain unknown.
The same error group has appeared across multiple application releases.
Suspected Replay checkout path
This is a hypothesis based on the timing, the frozen visual state, and inspection of the bundled Replay/rrweb source. We have not confirmed the exact throwing statement.
In error-buffer mode, Replay uses a 60,000 ms checkout interval. The observed failures occur close to that boundary and can repeat at approximately 60-second intervals.
The examined full-snapshot path appears to perform the following sequence:
lock mutation buffers
serialize the document
emit the full snapshot
unlock mutation buffers
The lock/snapshot/unlock sequence does not appear to protect the unlock operation with try/finally.
If Safari throws while the document is being serialized, the mutation buffers could remain locked. This would be consistent with the observed behavior: later DOM mutations disappear from the Replay while navigation, console, network, and other non-DOM events continue.
The rrweb callback wrapper also appears to mark internal errors and rethrow them. Replay then filters internal rrweb errors from normal capture. This may explain why the potentially useful TypeError is not reported with its original stack, while a downstream CustomEvent or empty object reaches the global promise-rejection handler.
The exact Safari DOM object or serialization operation that produces TypeError: Cannot call a class as a function has not been identified because the originating stack is unavailable.
User impact
No application malfunction has been observed. The current impact is on observability:
- Repeated noisy error events are generated.
- Multiple opaque issue groups may represent the same underlying failure.
- Replay becomes incomplete or misleading.
- Later user actions cannot be reconstructed visually even though their accompanying activity remains present.
Related issues reviewed
We searched open and closed issues. The closest reports do not describe the same behavior:
The automatically suggested issue #17873 concerns beforeErrorSampling behavior and does not describe DOM recording becoming frozen.
Questions
Could the Replay team advise:
- Can the full-snapshot path guarantee mutation-buffer unlocking with
try/finally if document serialization throws?
- Can Replay recover, retry, or stop cleanly after an unsuccessful periodic checkout?
- Can an internal rrweb exception be prevented from escaping as an opaque global promise rejection?
- Is there a supported diagnostic option that preserves the original rrweb exception and stack without recursively capturing Replay’s own errors?
- Are there known Safari DOM values or Web APIs that can produce
Cannot call a class as a function during a full snapshot?
- Would any additional debug logging be useful for identifying the precise serializer operation?
Priority
React with 👍 to help prioritize this issue. Please use comments to provide useful context, avoiding +1 or me too, to help us triage it.
Is there an existing issue for this?
How do you use Sentry?
Sentry Saas (sentry.io)
Which SDK are you using?
@sentry/angular
SDK Version
10.75.0
Framework Version
Angular: 22.1.7
Link to Sentry event
https://remotemdr-9a8bcf225.sentry.io/issues/5748192625/events/9e7fd03395c041b7b3f8721468a6b072/?project=4505866220732416
Reproduction Example/SDK Setup
We do not currently have a deterministic public reproduction. The issue has been observed across multiple production Replays in Safari on macOS.
The relevant SDK setup is equivalent to:
The Replay integration is added after the Angular application has bootstrapped. Additional
block,unblock, andunmaskselectors have been omitted because they only target ordinary application elements.The application uses Angular 22.1.7 without
zone.js.Steps to Reproduce
This is not yet deterministically reproducible outside production. The following sequence describes the behavior observed in affected Replays:
replaysSessionSampleRate: 0andreplaysOnErrorSampleRate: 1.0.unhandledrejectionevents in Sentry.In a representative Replay:
CustomEventrejection occurred approximately 60.1 seconds later.Most of the affected Replays we manually reviewed show the same frozen-visual-state pattern.
Expected Result
A failure during a periodic Replay checkout should be contained within Replay.
Specifically:
Actual Result
Around the point where the Replay visual state stops updating, Sentry captures:
and:
A correlated error appears shortly afterward as a
sentry.eventbreadcrumb:The originating
TypeErrordoes not have a useful stack. The top-level events contain only the globalunhandledrejectioncapture path.After the failure:
We reviewed additional Replays belonging to the same Sentry issue. Most of the reviewed Replays show this same mismatch between continuing application activity and a stale visual frame.
Additional Context
Observed scope
All sampled occurrences in this issue group were from Safari on macOS.
One representative affected Replay used Safari 26.6.2. This does not mean the issue reproduces on every Safari installation or every installation of Safari 26.6.2. The exact browser, DOM, and timing conditions required to trigger the failure remain unknown.
The same error group has appeared across multiple application releases.
Suspected Replay checkout path
This is a hypothesis based on the timing, the frozen visual state, and inspection of the bundled Replay/rrweb source. We have not confirmed the exact throwing statement.
In error-buffer mode, Replay uses a 60,000 ms checkout interval. The observed failures occur close to that boundary and can repeat at approximately 60-second intervals.
The examined full-snapshot path appears to perform the following sequence:
The lock/snapshot/unlock sequence does not appear to protect the unlock operation with
try/finally.If Safari throws while the document is being serialized, the mutation buffers could remain locked. This would be consistent with the observed behavior: later DOM mutations disappear from the Replay while navigation, console, network, and other non-DOM events continue.
The rrweb callback wrapper also appears to mark internal errors and rethrow them. Replay then filters internal rrweb errors from normal capture. This may explain why the potentially useful
TypeErroris not reported with its original stack, while a downstreamCustomEventor empty object reaches the global promise-rejection handler.The exact Safari DOM object or serialization operation that produces
TypeError: Cannot call a class as a functionhas not been identified because the originating stack is unavailable.User impact
No application malfunction has been observed. The current impact is on observability:
Related issues reviewed
We searched open and closed issues. The closest reports do not describe the same behavior:
The automatically suggested issue #17873 concerns
beforeErrorSamplingbehavior and does not describe DOM recording becoming frozen.Questions
Could the Replay team advise:
try/finallyif document serialization throws?Cannot call a class as a functionduring a full snapshot?Priority
React with 👍 to help prioritize this issue. Please use comments to provide useful context, avoiding
+1orme too, to help us triage it.