Troubleshooting

Troubleshoot and resolve common issues with the iOS Session Replay.

Why is Session Replay not running on iOS 26.0?

Due to potential masking issues introduced by Apple's Liquid Glass rendering changes in iOS 26.0, Session Replay is now automatically disabled on apps running iOS 26.0+ when built with Xcode 26.0 or later starting with Sentry Cocoa 8.57.0. This is a defensive measure to protect user privacy and prevent potential PII leaks until masking is reliably supported. Earlier SDK versions do not include this safeguard and may be affected by unreliable masking on iOS 26.0 when built with Xcode 26.0 (Liquid Glass). We recommend upgrading to 8.57.0+ or, if you can't upgrade the SDK, disabling Session Replay manually.

Session Replay will work normally if:

  • Your app runs on iOS versions prior to 26.0, OR
  • Your app is built with Xcode prior to 26.0, OR
  • Your app explicitly sets UIDesignRequiresCompatibility to YES in Info.plist

If you understand the PII risks and want to enable Session Replay anyway, you can override this behavior:

Copied
options.experimental.enableSessionReplayInUnreliableEnvironment = true

This experimental override option will be removed in a future minor version once the masking issues are resolved.

You can follow the ongoing work to fix masking for iOS 26.0 (Liquid Glass) in GitHub issue #6390.

Does Session Replay work with UIKit, SwiftUI or AppKit?

Session Replay works with both UIKit and SwiftUI.

Does Session Replay work on macOS, watchOS, tvOS and visionOS?

We don't actively prevent you from using Session Replay on these platforms, but we only officially support it on iOS. As a consequence, we can't make guarantees about its functionality and performance on other platforms.

Why are parts of my replay not masked?

Text views, input views, images, video players and webviews are all masked by default. Images with bundled assets aren't masked because the likelihood of these assets containing PII is low. If you encounter a view that should be masked by default, consider opening a GitHub issue.

What's the lowest version of iOS supported?

Session Replay recording happens even on the lowest version supported by the Sentry SDK, which is aligend with appstore support.

Why is my issue missing a replay?

An issue may be missing a replay because the user's device was offline while sessionSampleRate was specified, your project/organization was rate-limited, or (in rare cases) the device failed to capture the replay video.

AVFoundation views and layers are not rendered

Session Replay currently cannot capture content from AVFoundation views and layers. This includes:

  • AVPlayerLayer and other AVFoundation-backed video content
  • AVCaptureVideoPreviewLayer used for camera previews
  • Any media content rendered using AVFoundation components

As a result, camera views, video players, and other AVFoundation-based UI elements will not appear in session replays, even when using unmaskedViewClasses configuration.

We are aware of this limitation and are exploring potential solutions to support AVFoundation content in future releases.

You can follow the progress in this GitHub issue.

Photo pickers and camera views are not captured

Session Replay cannot capture photo picker interfaces (PHPickerViewController and UIImagePickerController) or camera views due to iOS limitations. These pickers use a remote view rendering process rather than being rendered directly in your app's view hierarchy.

On physical devices, photo pickers and camera views will not appear in session replays. On simulators, they may appear because simulators don't use the same remote rendering approach, but this behavior should not be relied upon.

This limitation exists at the iOS system level and cannot be worked around by configuring unmaskedViewClasses or other Session Replay settings.

Session Replay is not recording with custom window setup

If you have a custom window setup (e.g., multiple instances of UIWindow), you need to ensure that the Sentry SDK is able to find the correct window.

When using window scenes, make sure that the main window is assigned in your UIWindowSceneDelegate's window property.

Copied
final class SceneDelegate: NSObject, UIWindowSceneDelegate {

    var window: UIWindow?

    func scene(
        _ scene: UIScene,
        willConnectTo session: UISceneSession,
        options connectionOptions: UIScene.ConnectionOptions
    ) {
        guard let scene = scene as? UIWindowScene else { return }

        // Configure your windows here, e.g.
        let mainWindow = UIWindow(windowScene: scene)
        mainWindow.rootViewController = UIViewController()
        mainWindow.makeKeyAndVisible()

        // Do not forget to assign the window to the SceneDelegate's window property:
        self.window = mainWindow
    }
}

Alternatively, you can also create a custom proxy variable for the window:

Copied
final class SceneDelegate: NSObject, UIWindowSceneDelegate {

    var mainWindow: UIWindow?

    func scene(
        _ scene: UIScene,
        willConnectTo session: UISceneSession,
        options connectionOptions: UIScene.ConnectionOptions
    ) {
        guard let scene = scene as? UIWindowScene else { return }

        // Configure your windows here, e.g.
        let mainWindow = UIWindow(windowScene: scene)
        mainWindow.rootViewController = UIViewController()
        mainWindow.makeKeyAndVisible()

        // Do not forget to assign the window to the SceneDelegate's window property:
        self.window = mainWindow
    }

    // This is required to make sure that the Sentry SDK can find the correct window:

    var window: UIWindow? {
        get {
            return mainWindow
        }
        set {
            mainWindow = newValue
        }
    }
}
App crashes during Session Replay or screenshot capture

If your app crashes during Session Replay recording or screenshot capture, it may be due to problematic view hierarchies that contain layers that crash when accessed during traversal.

Some view types contain layers that cause fatal errors when their sublayers property is accessed. For example, CameraUI.ChromeSwiftUIView on iOS 26+ contains CameraUI.ModeLoupeLayer instances that crash during view hierarchy traversal.

You can prevent these crashes by configuring the SDK to skip traversing the subtree of problematic view types. The view itself will still be redacted, but the SDK won't attempt to traverse its children, preventing crashes.

To add a problematic view type to the ignore set:

Copied
options.sessionReplay.excludeViewTypeFromSubtreeTraversal("MyProblematicView")

The view type identifier uses partial string matching against the result of type(of: view).description(). This means if you exclude "MyView", it will match "MyApp.MyView", "MyViewSubclass", etc. You can find the exact type name by inspecting the view hierarchy or checking crash logs.

On iOS 26+, CameraUI.ChromeSwiftUIView is automatically included in the ignore set by default to prevent crashes. If you're experiencing crashes with other view types, add them to the ignore set using the method above.

For more information, see the Ignore View Types from Subtree Traversal section in the custom masking documentation.

Masking limitations with AsyncDisplayKit/Texture

AsyncDisplayKit (now archived) and its successor Texture use a custom rendering pipeline that relies on layers rather than standard UIKit components. This architecture prevents Session Replay's default masking options (maskAllImages and maskAllText) from working correctly.

Texture uses _ASDisplayView and _ASDisplayLayer classes that forward rendering to associated nodes, rather than using standard UIKit view hierarchies. The SDK's default masking mechanisms rely on identifying standard UIKit components (like UIImageView and UILabel), which don't exist in Texture's custom rendering pipeline. As a result, content rendered through Texture nodes may not be masked by default.

For example, when inspecting the view hierarchy, you'll see views like ButtonNode-View with _ASDisplayLayer sublayers rather than standard UIKit components:

Copied
<SentryMaskingExample.ButtonNode-View: 0x103b35e80; node = <SentryMaskingExample.ButtonNode: 0x10483b600>; ...>
   | <_ASDisplayLayer: 0x60000213ab20> (layer)
   | <_ASDisplayLayer: 0x600002139900> (layer)

You can work around this limitation by using masking by view instance. Access the view property of your ASDisplayNode instances and apply masking to those specific views:

Copied
// Access the view from your ASDisplayNode
let nodeView = myASDisplayNode.view
SentrySDK.replay.maskView(view: nodeView)

Note that this approach masks entire nodes rather than individual subcomponents. If you need more granular control, you may need to mask parent nodes or use the supernode property to mask larger sections of your UI.

  • Swizzling is not supported. While method swizzling could theoretically intercept ASImageNode and ASTextNode rendering, Sentry does not support or recommend this approach due to stability and maintenance concerns.
  • Layer-backed nodes. Some layer-backed nodes may still not be properly masked even when using view instance masking, as they render directly to layers rather than views.
Was this helpful?
Help improve this content
Our documentation is open source and available on GitHub. Your contributions are welcome, whether fixing a typo (drat!) or suggesting an update ("yeah, this would be better").