{"record":{"id":"f1ca4110beffd26e","repo":"alyssaxuu/screenity","slug":"offscreen-tab-stream-failed","errorCode":null,"errorMessage":"offscreen tab stream failed","messagePattern":"offscreen tab stream failed","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/pages/CloudRecorder/CloudRecorder.jsx","lineNumber":8146,"sourceCode":"    } catch (err) {\n      sendRecordingError(\"Failed to setup streaming: \" + err.message);\n    }\n  };\n\n  const getStreamID = async (id) => {\n    try {\n      let streamId;\n      if (IS_OFFSCREEN_HOST) {\n        // chrome.tabCapture is not callable from offscreen; delegate to SW.\n        const response = await chrome.runtime\n          .sendMessage({\n            type: \"offscreen-request-stream\",\n            mode: \"tab\",\n            targetTabId: id,\n          })\n          .catch((err) => ({ ok: false, error: String(err) }));\n        if (!response?.ok || !response.streamId) {\n          throw new Error(response?.error || \"offscreen tab stream failed\");\n        }\n        streamId = response.streamId;\n      } else {\n        streamId = await chrome.tabCapture.getMediaStreamId({\n          targetTabId: id,\n        });\n      }\n      tabID.current = streamId;\n    } catch (err) {\n      sendRecordingError(\"Failed to get stream ID: \" + err.message);\n    }\n  };\n\n  useEffect(() => {\n    if (!IS_IFRAME_CONTEXT) return;\n\n    const sendReady = () => {\n      window.parent.postMessage(","sourceCodeStart":8128,"sourceCodeEnd":8164,"githubUrl":"https://github.com/alyssaxuu/screenity/blob/512606387b8d07dda5e63bb428bd063f0a2a3ed0/src/pages/CloudRecorder/CloudRecorder.jsx#L8128-L8164","documentation":"getStreamID() runs in an offscreen document where chrome.tabCapture is not callable, so it asks the service worker via an 'offscreen-request-stream' message. This error is thrown when the SW response is absent, not ok, or lacks a streamId — meaning tab capture could not be granted through the SW delegate.","triggerScenarios":"chrome.runtime.sendMessage({type:'offscreen-request-stream', mode:'tab', targetTabId}) rejects (SW unavailable, message port closed) producing {ok:false,error:String(err)}, or the SW replies ok:false / without streamId because chrome.tabCapture.getMediaStreamId failed (tab not active, no tabCapture permission, capture already in progress).","commonSituations":"Service worker asleep or crashed so the message promise rejects with 'Could not establish connection', target tab closed or navigated before the request, chrome.tabCapture.getMediaStreamId returning undefined due to user gesture/activeTab requirements, or Manifest V3 offscreen document restrictions.","solutions":["Check the SW responded at all: a rejected sendMessage means the service worker is dead — ensure it's alive/awake before the request","Verify tabCapture permission in the manifest and that the target tab is still open and capturable","Confirm the SW's offscreen-request-stream handler calls chrome.tabCapture.getMediaStreamId({targetTabId}) and returns {ok:true, streamId}","Retry once after SW wake-up, mirroring the sendOnce retry pattern used for scene creation","Fall back to the non-offscreen path (chrome.tabCapture.getMediaStreamId directly) when not running in an offscreen host"],"exampleFix":"// before\nif (!response?.ok || !response.streamId) {\n  throw new Error(response?.error || \"offscreen tab stream failed\");\n}\n// after\nif (!response?.ok || !response.streamId) {\n  await new Promise(r => setTimeout(r, 300)); // let SW wake\n  response = await chrome.runtime.sendMessage({ type: \"offscreen-request-stream\", mode: \"tab\", targetTabId: id }).catch(e => ({ ok: false, error: String(e) }));\n}\nif (!response?.ok || !response.streamId) {\n  throw new Error(response?.error || \"offscreen tab stream failed\");\n}","handlingStrategy":"fallback","validationCode":"async function canRequestTabStream(tabId) {\n  if (!chrome.runtime?.id) return false; // extension context invalidated\n  const tab = await chrome.tabs.get(tabId).catch(() => null);\n  return Boolean(tab && !tab.discarded);\n}","typeGuard":"function streamResponseOk(res) {\n  return typeof res === \"object\" && res !== null && res.ok === true && typeof res.streamId === \"string\" && res.streamId.length > 0;\n}","tryCatchPattern":"try {\n  const res = await chrome.runtime.sendMessage({ type: \"offscreen-request-stream\", mode: \"tab\", targetTabId: id });\n  if (!streamResponseOk(res)) throw new Error(res?.error || \"offscreen tab stream failed\");\n  streamId = res.streamId;\n} catch (err) {\n  // wake SW and retry once, then surface to user\n  await pingServiceWorker();\n  const res2 = await chrome.runtime.sendMessage({ type: \"offscreen-request-stream\", mode: \"tab\", targetTabId: id }).catch(e => ({ ok: false, error: String(e) }));\n  if (!streamResponseOk(res2)) sendRecordingError(\"Failed to get stream ID: \" + (res2?.error || err.message));\n  else streamId = res2.streamId;\n}","preventionTips":["Ping/keep the service worker alive before sending offscreen-request-stream","Verify the target tab still exists and is capturable before requesting the streamId","Declare tabCapture (and activeTab where needed) in the manifest","Always return a structured {ok, streamId, error} from the SW handler so failures are distinguishable"],"tags":["chrome-extension","tab-capture","offscreen","service-worker","messaging"],"backgroundTag":"offscreen-message-stream-failed","analyzedSha":"512606387b8d07dda5e63bb428bd063f0a2a3ed0","analyzedAt":"2026-09-02T20:59:09.419Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-10T02:17:09.455Z"}