Fix YouTube live Picture-in-Picture Error 153 - #1758
Conversation
Initialize the native PiP document from its opener before rendering embeds so YouTube receives the required Referer header. Cover real browser requests and PiP lifecycle without loading external providers in CI. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Copilot review overview
🟢 Approval recommended
No unresolved review comments remain, and the supplied validation covers the fix.
Review effort: Lite
Findings: None
What changed in this PR
Fixes YouTube Error 153 in native Document Picture-in-Picture by preserving the site referrer.
Changes:
- Initializes the PiP document with
document.open()/document.close(). - Applies an explicit iframe referrer policy.
- Adds Chromium E2E coverage for fallback, switching, navigation, and cleanup.
| File | Description |
|---|---|
src/frontend/tests/e2e/live-pip.spec.ts |
Verifies referrers, provider switching, navigation, cleanup, and reopening. |
src/frontend/src/components/LivePip.astro |
Initializes the PiP document and configures iframe referrer handling. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Frontend HTML artifact readyThe latest frontend build uploaded the This comment updates automatically when a new frontend build artifact is uploaded. |
| pipWindow.document.open(); | ||
| pipWindow.document.close(); |
There was a problem hiding this comment.
This seems like a hack. Is this the real way to handle the problem?
There was a problem hiding this comment.
Fair question. This is a standards-defined workaround for the missing referrer, not a YouTube- or Chrome-documented PiP recipe. I should distinguish those rather than imply this is the official PiP solution.
The relevant behavior is explicit in the HTML specification's document open steps:
- It obtains
entryDocumentfrom the calling environment and requires the target document to be same-origin. - For a fully active target document, it says: “Let newURL be a copy of entryDocument's URL.” If the documents differ, it removes the fragment, then runs the URL and history update steps for the target document with that URL.
- It sets the target document's “is initial about:blank” flag to false.
- It clears the target document's contents and its DOM/window event listeners, then creates an HTML parser.
document.close()closes that input stream; we subsequently create the iframe through the DOM API, withoutdocument.write()or HTML string injection.
That URL change is the part we need: PiP initially contains an about:blank document, and the direct YouTube embed request had no HTTP Referer. Calling open() from the opener gives the PiP document the opener's URL, allowing Chromium to generate the header normally. The explicit strict-origin-when-cross-origin policy sends only our origin to YouTube, not the documentation path or query. This satisfies YouTube's embedded-player identification requirement.
The destructive effects are why these calls run only on the newly created, empty PiP document, before registering the pagehide listener or adding the iframe—never during provider switches or reuse of an existing PiP window.
I compared alternatives in a real native PiP window against the livestream. Referrer policy alone, adding origin, a srcdoc wrapper, and a blob wrapper all still produced Error 153. A hosted same-origin wrapper also worked, but adds a page/request and nested iframe. The open/close initialization restored Referer: https://aspire.dev/ and actual playback without that extra structure.
The new tests use a real native PiP window and inspect the browser-generated request, stubbing only the provider response. Both video-ID and channel-fallback cases failed with a null referrer before the fix and pass after it; they also cover provider switching, client navigation, and close/reopen cleanup. On that evidence, I prefer this smaller, standards-defined workaround to the hosted wrapper.
Auto-replied by the GitHub Copilot app
Eric Erhardt (eerhardt)
left a comment
There was a problem hiding this comment.
Seems like a hack, but if you (and your agent) are convinced it is the correct approach, let's do it.
Summary
Fix YouTube's "Video player configuration error — Error 153" in native Document Picture-in-Picture without adding a wrapper page or changing Twitch's player URL.
The live-status API returned the correct live video ID, but the iframe created directly in PiP's initial
about:blankdocument sent no HTTPReferer. YouTube requires that identification; Twitch already receives its explicitparentparameter.Initialize the newly opened PiP document with
document.open()/document.close()before rendering or attaching lifecycle listeners, and explicitly set the iframe'sstrict-origin-when-cross-originreferrer policy. This is five production lines, including the explanation. Nodocument.write(), additional route, extra request, nested iframe, or new dependency is needed.Why this approach
The HTML document-open algorithm copies the calling document's URL to the target document. It also clears listeners, which is why initialization occurs before registering the existing
pagehidehandler and only once per new PiP window. The iframe policy sends only the site origin to YouTube, not the documentation path or query string.Browser experiments against the current aspire.dev livestream compared:
originplayer parametersrcdocwrapperThis follows YouTube's embedded-player identification requirement.
Third-party links and affiliations
None. The site continues to use the existing YouTube and Twitch endpoints.
Validation
Referer: nulland pass with the fix.pnpm --dir src/frontend exec playwright test tests/e2e/live-pip.spec.ts tests/e2e/live-status.spec.ts --workers=2 --reporter=line: 25 passed, 11 expected viewport-specific skips across desktop/tablet/mobile.Referer: https://aspire.dev/, video readyState 4, paused false).