{"record":{"id":"995087a17c0bddf2","repo":"jitsi/jitsi-meet","slug":"wrongsurfaceselected","errorCode":null,"errorMessage":"WrongSurfaceSelected","messagePattern":"WrongSurfaceSelected","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"react/features/recording/components/Recording/LocalRecordingManager.web.ts","lineNumber":224,"sourceCode":"                    restrictOwnAudio: false,\n                    // @ts-ignore\n                    suppressLocalAudioPlayback: false,\n                },\n                // @ts-ignore\n                preferCurrentTab: true,\n                surfaceSwitching: 'exclude'\n            });\n\n            const gdmVideoTrack = gdmStream.getVideoTracks()[0];\n\n            if (supportsCaptureHandle) {\n                const isBrowser = gdmVideoTrack.getSettings().displaySurface === 'browser';\n                const matchesHandle = (supportsCaptureHandle // @ts-ignore\n                    && gdmVideoTrack.getCaptureHandle()?.handle === `JitsiMeet-${tabId}`);\n\n                if (!isBrowser || !matchesHandle) {\n                    gdmStream.getTracks().forEach((track: MediaStreamTrack) => track.stop());\n                    throw new Error('WrongSurfaceSelected');\n                }\n            }\n\n            this.initializeAudioMixer();\n\n            const gdmAudioTrack = gdmStream.getAudioTracks()[0];\n\n            if (!gdmAudioTrack) {\n                throw new Error('NoAudioTrackFound');\n            }\n\n            this.addAudioTrackToLocalRecording(gdmAudioTrack);\n\n            const localAudioTrack = getLocalTrack(tracks, MEDIA_TYPE.AUDIO)?.jitsiTrack?.track;\n\n            if (localAudioTrack) {\n                this.addAudioTrackToLocalRecording(localAudioTrack);\n            }","sourceCodeStart":206,"sourceCodeEnd":242,"githubUrl":"https://github.com/jitsi/jitsi-meet/blob/98de6219cc7ddbe07ace9fde045aff90a242ba01/react/features/recording/components/Recording/LocalRecordingManager.web.ts#L206-L242","documentation":"Thrown when the user, during the getDisplayMedia prompt for local recording with self-recording, picks a surface that is not the current browser tab. Jitsi requires the recording to capture the app's own tab, verified via displaySurface === 'browser' and the Capture Handle API matching handle 'JitsiMeet-<tabId>'. Any other choice (window, monitor, different tab) stops the acquired tracks and throws 'WrongSurfaceSelected'.","triggerScenarios":"startLocalRecording with selfRecording enabled calls getDisplayMedia; the user selects 'Window' or 'Entire Screen' instead of the browser tab, or selects a different browser tab whose captureHandle doesn't equal `JitsiMeet-${tabId}`, or the browser doesn't support Capture Handle / doesn't transmit the handle (matchesHandle false).","commonSituations":"User confusion in the Chrome screen-share dialog; Firefox/Safari lacking capture handle support (supportsCaptureHandle false makes matchesHandle false and the check fail); sites without a capture handle config; tabId mismatch after duplication.","solutions":["Instruct the user (pre-prompt UI/notification) that they must select 'This Tab' / the browser tab option in the picker","Verify browser support: requires Chromium with Capture Handle API; prefer/require Chrome and ensure the deployment injects the capture handle (JitsiMeet-<tabId>) metadata so getCaptureHandle() matches","Catch the error and restart the flow with a clear message so the user can retry and pick the right surface","If cross-browser support is needed, relax the matchesHandle check only when supportsCaptureHandle is false, relying on displaySurface alone"],"exampleFix":"// before\nif (!isBrowser || !matchesHandle) {\n    gdmStream.getTracks().forEach(t => t.stop());\n    throw new Error('WrongSurfaceSelected');\n}\n\n// after (guide the user and let them retry)\nif (!isBrowser || !matchesHandle) {\n    gdmStream.getTracks().forEach(t => t.stop());\n    dispatch(showErrorNotification({\n        titleKey: 'localRecording.wrongSurface',\n        descriptionKey: 'localRecording.selectThisTab'\n    }));\n    return; // or re-open the picker\n}","handlingStrategy":"validation","validationCode":"const supports = 'getCaptureHandle' in videoTrack && browser.isChromeBased;\n// Before the picker: notify user to pick 'This Tab'\nshowInfoNotification({ descriptionKey: 'localRecording.selectThisTab' });\nawait manager.startLocalRecording(); // getDisplayMedia with preferCurrentTab: true","typeGuard":"const isCorrectSurface = (t: MediaStreamTrack, tabId: string): boolean =>\n    t.getSettings().displaySurface === 'browser'\n    && (t as any).getCaptureHandle?.()?.handle === `JitsiMeet-${tabId}`;","tryCatchPattern":"try {\n    await manager.startLocalRecording();\n} catch (e: unknown) {\n    if (e instanceof Error && e.message === 'WrongSurfaceSelected') {\n        showWarning('Please select this browser tab when sharing.');\n        // optionally re-run the flow once\n    } else {\n        throw e;\n    }\n}","preventionTips":["Show a pre-prompt hint telling users to select 'This Tab'","Use getDisplayMedia({ video: true, audio: true, preferCurrentTab: true }) on Chromium","Require a Chromium browser that supports the Capture Handle API for self-recording","Ensure the app page emits the capture handle metadata (JitsiMeet-<tabId>)"],"tags":["getdisplaymedia","screen-capture","capture-handle","browser-support","local-recording"],"backgroundTag":"wrong-screen-capture-surface","analyzedSha":"98de6219cc7ddbe07ace9fde045aff90a242ba01","analyzedAt":"2026-08-28T15:15:59.482Z","schemaVersion":2},"datasetVersion":"2026-08-28T16:17:29.566Z"}