Current Behavior:
TypeError: Cannot read properties of null (reading 'addEventListener') thrown from EventsController.add when an HLS-sourced <MediaPlayer> is unmounted while the HLS provider chunk is still being dynamically imported.
The crash has two compounding causes:
1. Stale-target read across await in HLSProviderLoader.load (packages/vidstack/src/providers/video/hls/loader.ts):
async load(context) {
if (__SERVER__) throw Error(...);
return new (await import('./provider')).HLSProvider(this.target, context);
// ^^^^^^^^^^^ read AFTER await
}
this.target is mutable. Between dispatching the dynamic import and its resolution, MediaProvider#runLoader(null) (triggered by the <video> ref callback firing with null on unmount) reassigns loader.target = null. The post-await read sees the null, and new HLSProvider(null, ctx) is constructed.
2. canUsePictureInPicture returns true for null input (packages/vidstack/src/utils/support.ts):
export function canUsePictureInPicture(video: HTMLVideoElement | null): boolean {
if (__SERVER__) return false;
return !!document.pictureInPictureEnabled && !video?.disablePictureInPicture;
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
// !undefined === true when video is null
}
This gates the unguarded branch in VideoProvider's constructor:
} else if (canUsePictureInPicture(video)) {
this.pictureInPicture = new VideoPictureInPicture(video, ctx); // video is null
}
new EventsController(null).add('enterpictureinpicture', ...) then dereferences null and crashes.
This is the same predicate-bug class as #989, which fixed canUseVideoPresentation but did not extend the fix to canUsePictureInPicture. The last comment on #989 from @dinonondi reports the same null PiP crash. The HLSProviderLoader race underneath it appears not to have been reported separately.
Expected Behavior:
HLSProviderLoader.load() should either return a provider for the target it was configured with, or return null to signal the target was invalidated mid-load — so the caller can skip broadcasting a doomed provider. canUsePictureInPicture should reject null inputs, matching the contract its name implies and the fix already applied to canUseVideoPresentation in aff2d294.
Steps To Reproduce:
- Render an HLS source in
<MediaPlayer> — e.g. src="https://.../manifest/video.m3u8" with <MediaProvider /> inside.
- Open DevTools → Network → enable Disable cache.
- Set throttling to Slow 4G (or any preset where the HLS provider chunk takes >500 ms to download).
- Hard-reload the page so the HLS chunk is uncached in the ESM module registry.
- Mount the player, then unmount it before the HLS chunk finishes downloading. With throttling on, this is a reliable several-second window.
- When the chunk finishes loading, the
addEventListener crash fires.
Minimal reproducer:
function Repro() {
const [show, setShow] = useState(false)
return (
<>
<button onClick={() => setShow(s => !s)}>Toggle</button>
{show && (
<MediaPlayer src="https://.../manifest/video.m3u8">
<MediaProvider />
</MediaPlayer>
)}
</>
)
}
Click "Toggle" once → wait until the HLS chunk is still pending in the Network panel → click "Toggle" again → observe the crash when the chunk arrives.
Environment:
@vidstack/react: 1.12.13 (next tag)
hls.js: 1.6.15
- React: 19.1.0
- Node: 24.11.1
- Browser: Chromium 136 (Electron 40.1)
- OS: macOS 15
Current Behavior:
TypeError: Cannot read properties of null (reading 'addEventListener')thrown fromEventsController.addwhen an HLS-sourced<MediaPlayer>is unmounted while the HLS provider chunk is still being dynamically imported.The crash has two compounding causes:
1. Stale-target read across
awaitinHLSProviderLoader.load(packages/vidstack/src/providers/video/hls/loader.ts):this.targetis mutable. Between dispatching the dynamic import and its resolution,MediaProvider#runLoader(null)(triggered by the<video>ref callback firing withnullon unmount) reassignsloader.target = null. The post-await read sees the null, andnew HLSProvider(null, ctx)is constructed.2.
canUsePictureInPicturereturnstruefor null input (packages/vidstack/src/utils/support.ts):This gates the unguarded branch in
VideoProvider's constructor:new EventsController(null).add('enterpictureinpicture', ...)then dereferences null and crashes.This is the same predicate-bug class as #989, which fixed
canUseVideoPresentationbut did not extend the fix tocanUsePictureInPicture. The last comment on #989 from@dinonondireports the same null PiP crash. TheHLSProviderLoaderrace underneath it appears not to have been reported separately.Expected Behavior:
HLSProviderLoader.load()should either return a provider for the target it was configured with, or returnnullto signal the target was invalidated mid-load — so the caller can skip broadcasting a doomed provider.canUsePictureInPictureshould reject null inputs, matching the contract its name implies and the fix already applied tocanUseVideoPresentationinaff2d294.Steps To Reproduce:
<MediaPlayer>— e.g.src="https://.../manifest/video.m3u8"with<MediaProvider />inside.addEventListenercrash fires.Minimal reproducer:
Click "Toggle" once → wait until the HLS chunk is still pending in the Network panel → click "Toggle" again → observe the crash when the chunk arrives.
Environment:
@vidstack/react: 1.12.13 (nexttag)hls.js: 1.6.15