Desktop app auto-previews links causing unwanted side effects
Cluster 47500 · apps/desktop. Data as of
1 open item. Fix merged in #49515 on 20 Jun 2026. Check it covers your case.
The short answer
- Known?
- Yes. 3 related items grouped here, 1 issue already closed.
- Fix event
- A fix was merged to main in PR #49515. Check it covers your case. No release data is tracked.
- Tracker
- The maintainers' tracker marks this
partially resolved, deciding PR #49515. That is separate from the fix event. - Still open
- 1 issue and 0 PRs.
Next step: read #49515, then check whether your report is the one still open.
Read #49515Pick up a Lab taskThese issues concern the desktop app automatically previewing or loading external links in chat, which can trigger unintended behaviors such as audio autoplay and custom protocol handler activation.
Open issues1
- Issue#47500[Bug]: Desktop app auto-previews external links in chat, triggering custom protocol handlers (e.g. bitbrowser://)
Open PRs0
Fixes on record
-
#49515 fix(desktop): mute hidden link-title window so historical links don't autoplay audio
files:
apps/desktop/electron/link-title-window.cjs,apps/desktop/electron/link-title-window.test.cjs,apps/desktop/electron/main.cjs,apps/desktop/package.jsonShow the diff
-
apps/desktop/electron/link-title-window.cjs@@ -0,0 +1,42 @@ +'use strict' + +// Hidden BrowserWindow used by tier-2 link-title resolution: when curl can't +// read a page <title> (bot walls, JS-rendered pages), we briefly load the URL +// in an offscreen window and read its title. That window loads arbitrary +// user-linked pages — including YouTube/`watch` URLs that autoplay — so it must +// never be allowed to emit sound. + +function linkTitleWindowOptions(partitionSession) { + return { + show: false, + width: 1280, + height: 800, + webPreferences: { + backgroundThrottling: false, + contextIsolation: true, + javascript: true, + nodeIntegration: false, + sandbox: true, + session: partitionSession, + webSecurity: true + } + } +} + +// Create the offscreen title-fetch window and immediately mute it. Without the +// mute, autoplaying media on the loaded page (e.g. a YouTube link) leaks ~2s of +// audio every time a session containing such links is re-rendered. See #49505. +function createLinkTitleWindow(BrowserWindow, partitionSession) { + const window = new BrowserWindow(linkTitleWindowOptions(partitionSession)) + + try { + window.webContents.setAudioMuted(true) + } catch { + // webContents may be unavailable in degraded/headless environments; muting + // is best-effort and the window is destroyed within a few seconds anyway. + } + + return window +} + +module.exports = { createLinkTitleWindow, linkTitleWindowOptions } -
apps/desktop/electron/link-title-window.test.cjs@@ -0,0 +1,56 @@ +const assert = require('node:assert/strict') +const test = require('node:test') + +const { createLinkTitleWindow, linkTitleWindowOptions } = require('./link-title-window.cjs') + +function makeFakeBrowserWindow() { + const calls = { audioMuted: [] } + const FakeBrowserWindow = function (options) { + this.options = options + this.webContents = { + setAudioMuted(value) { + calls.audioMuted.push(value) + } + } + } + + return { FakeBrowserWindow, calls } +} + +test('linkTitleWindowOptions keeps the offscreen, hardened defaults', () => { + const session = { id: 'link-titles' } + const options = linkTitleWindowOptions(session) + + assert.equal(options.show, false) + assert.equal(options.webPreferences.session, session) + assert.equal(options.webPreferences.contextIsolation, true) + assert.equal(options.webPreferences.sandbox, true) + assert.equal(options.webPreferences.nodeIntegration, false) +}) + +test('createLinkTitleWindow mutes audio so historical links never autoplay sound', () => { + // Regression for #49505: the hidden title-fetch window loaded YouTube/watch + // URLs (to read their <title>) without muting, leaking ~2s of audio on every + // history re-render. + const { FakeBrowserWindow, calls } = makeFakeBrowserWindow() + + const window = createLinkTitleWindow(FakeBrowserWindow, { id: 'link-titles' }) + + assert.ok(window instanceof FakeBrowserWindow) + assert.deepEqual(calls.audioMuted, [true]) +}) + +test('createLinkTitleWindow still returns the window if muting throws', () => { + const ThrowingBrowserWindow = function (options) { + this.options = options + this.webContents = { + setAudioMuted() { + throw new Error('webContents unavailable') + } + } + } + + const window = createLinkTitleWindow(ThrowingBrowserWindow, { id: 'link-titles' }) + + assert.ok(window instanceof ThrowingBrowserWindow) +}) -
apps/desktop/electron/main.cjs@@ -34,6 +34,7 @@ const { SESSION_WINDOW_MIN_WIDTH } = require('./session-windows.cjs') const { canImportHermesCli, verifyHermesCli } = require('./backend-probes.cjs') +const { createLinkTitleWindow } = require('./link-title-window.cjs') const { probeGatewayWebSocket } = require('./gateway-ws-probe.cjs') const { adoptServedDashboardToken } = require('./dashboard-token.cjs') const { waitForDashboardPort } = require('./backend-ready.cjs') @@ -2980,20 +2981,7 @@ function runRenderTitleJob(rawUrl) { } try { - window = new BrowserWindow({ - show: false, - width: 1280, - height: 800, - webPreferences: { - backgroundThrottling: false, - contextIsolation: true, - javascript: true, - nodeIntegration: false, - sandbox: true, - session: partitionSession, - webSecurity: true - } - }) + window = createLinkTitleWindow(BrowserWindow, partitionSession) } catch { return finish('') } -
apps/desktop/package.json@@ -37,7 +37,7 @@ "test:desktop:nsis": "node scripts/test-desktop.mjs nsis", "test:desktop:existing": "node scripts/test-desktop.mjs existing", "test:desktop:fresh": "node scripts/test-desktop.mjs fresh", - "test:desktop:platforms": "node --test electron/bootstrap-platform.test.cjs electron/hardening.test.cjs electron/backend-env.test.cjs electron/backend-probes.test.cjs electron/bootstrap-runner.test.cjs electron/connection-config.test.cjs electron/dashboard-token.test.cjs electron/gateway-ws-probe.test.cjs electron/oauth-net-request.test.cjs electron/desktop-uninstall.test.cjs electron/session-windows.test.cjs electron/workspace-cwd.test.cjs electron/fs-read-dir.test.cjs electron/git-root.test.cjs electron/windows-child-process.test.cjs electron/update-remote.test.cjs electron/update-rebuild.test.cjs electron/windows-user-env.test.cjs", + "test:desktop:platforms": "node --test electron/bootstrap-platform.test.cjs electron/hardening.test.cjs electron/backend-env.test.cjs electron/backend-probes.test.cjs electron/bootstrap-runner.test.cjs electron/connection-config.test.cjs electron/dashboard-token.test.cjs electron/gateway-ws-probe.test.cjs electron/oauth-net-request.test.cjs electron/desktop-uninstall.test.cjs electron/session-windows.test.cjs electron/link-title-window.test.cjs electron/workspace-cwd.test.cjs electron/fs-read-dir.test.cjs electron/git-root.test.cjs electron/windows-child-process.test.cjs electron/update-remote.test.cjs electron/update-rebuild.test.cjs electron/windows-user-env.test.cjs", "typecheck": "tsc -p . --noEmit", "lint": "eslint src/ electron/", "lint:fix": "eslint src/ electron/ --fix",
-
Closed issues · 1
- Closed#49505[Bug]: Desktop app auto-plays audio from YouTube links
How this cluster was grouped
-
Link evidence (
partly structural links): some members are connected by structural evidence (shared changed files, or a closing/superseded reference); the rest are joined by an automated relevance judgement over their text. - Members outside that structural core may belong to a different underlying problem.
-
Pairwise link density
0.45: the share of item pairs that carry a link, counting a structural link in full and a relevance-only link at 0.36.1.00means every pair is linked.
Pick up a Lab task
Check whether a fix covers the open issues here, with your own agent. Bring back evidence for maintainers.
Open the LabAt a glance
- Subsystem
- apps/desktop
- Merged PRs
- #49515
- Closed issues
- 1
- Attempts
- 0 PRs closed without merging
- Fix link checked
- 20 Jun 2026