{"record":{"id":"91d68f565dd16b9e","repo":"gildas-lormeau/SingleFile","slug":"singlefile-capture-was-not-approved-for-this-exten","errorCode":null,"errorMessage":"SingleFile capture was not approved for this extension","messagePattern":"SingleFile capture was not approved for this extension","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/core/bg/external-messages.js","lineNumber":124,"sourceCode":"\t\tconst tabs = await browser.tabs.query({ currentWindow: true, active: true });\n\t\tawait business.saveSelectedLinks(tabs[0]);\n\t} else if (message == ACTION_SAVE_SELECTED) {\n\t\tconst tabs = await browser.tabs.query({ currentWindow: true, active: true });\n\t\tawait business.saveTabs(tabs, { selected: true });\n\t} else if (message == ACTION_SAVE_SELECTED_TABS) {\n\t\tconst tabs = await queryTabs({ currentWindow: true, highlighted: true });\n\t\tawait business.saveTabs(tabs);\n\t} else if (message == ACTION_SAVE_UNPINNED_TABS) {\n\t\tconst tabs = await queryTabs({ currentWindow: true, pinned: false });\n\t\tawait business.saveTabs(tabs);\n\t} else if (message == ACTION_SAVE_ALL_TABS) {\n\t\tconst tabs = await queryTabs({ currentWindow: true });\n\t\tawait business.saveTabs(tabs);\n\t} else if (message && message.method == METHOD_CAPTURE_PAGE) {\n\t\tconst captureConfig = getCaptureConfig(message);\n\t\tconst permissionGranted = await externalCapturePermissions.requestPermission(sender, message);\n\t\tif (!permissionGranted) {\n\t\t\tthrow new Error(\"SingleFile capture was not approved for this extension\");\n\t\t}\n\t\tconst currentTab = message.tabId\n\t\t\t? await browser.tabs.get(message.tabId)\n\t\t\t: (await browser.tabs.query({ currentWindow: true, active: true }))[0];\n\t\tif (!currentTab) {\n\t\t\treturn false;\n\t\t}\n\t\treturn business.captureTab(currentTab, captureConfig);\n\t} else if (message && message.method) {\n\t\tconst tabs = await browser.tabs.query({ currentWindow: true, active: true });\n\t\tconst currentTab = tabs[0];\n\t\tif (currentTab) {\n\t\t\treturn autosave.onMessageExternal(message, currentTab, sender);\n\t\t} else {\n\t\t\treturn false;\n\t\t}\n\t}\n}","sourceCodeStart":106,"sourceCodeEnd":142,"githubUrl":"https://github.com/gildas-lormeau/SingleFile/blob/517fb7c5cf2096d89933b747e862d8ecf616a9f9/src/core/bg/external-messages.js#L106-L142","documentation":"When an external message requests a page capture (METHOD_CAPTURE_PAGE), SingleFile asks the user (or checks stored rules) via externalCapturePermissions.requestPermission whether this calling extension/site is allowed. If permission is not granted, the handler throws 'SingleFile capture was not approved for this extension'. This is an explicit approval gate for third-party capture requests.","triggerScenarios":"An external page/extension sends a capture request and requestPermission returns false — the user denied the prompt, no stored permission exists for the sender's origin, or auto-accept rules don't cover the sender.","commonSituations":"Integrating SingleFile capture into another web app or extension; the user previously clicked 'deny' so the choice is remembered; testing capture calls from a localhost origin that was never approved.","solutions":["Open SingleFile's options page and add the requesting origin/extension to the allowed external capture permissions, then retry.","Trigger the request again and approve the permission prompt when shown.","Clear any previously denied permission entry for the sender in stored config so the prompt is re-shown.","Verify the sender identity (extension id / origin) matches what was actually approved."],"exampleFix":"// before: fire and forget capture request that may be denied\nbrowser.runtime.sendMessage(otherExtId, { method: 'capture', ... });\n// after: catch and guide user to grant permission\ntry {\n  await browser.runtime.sendMessage(otherExtId, { method: 'capture', ... });\n} catch (e) {\n  if (String(e.message).includes('not approved')) {\n    alert('Open SingleFile options and approve this site for external capture.');\n  }\n}","handlingStrategy":"try-catch","validationCode":"// check stored permission config before requesting capture\nconst perms = await browser.runtime.sendMessage(singleFileId, { method: 'externalCapture.getPermissions' });\nconst approved = perms && perms.some(p => location.origin.startsWith(p.origin));","typeGuard":null,"tryCatchPattern":"try {\n  await browser.runtime.sendMessage(singleFileId, { method: 'capture', ... });\n} catch (e) {\n  if (String(e.message).includes('not approved')) {\n    // surface UI telling the user to approve this origin in SingleFile options\n  }\n}","preventionTips":["Pre-approve your origin in SingleFile's external capture permissions options","Handle the denial path in your integration UI","Avoid re-requesting repeatedly after a denial; guide the user to settings instead"],"tags":["permissions","extension-messaging","user-consent"],"backgroundTag":"permission-not-granted","analyzedSha":"517fb7c5cf2096d89933b747e862d8ecf616a9f9","analyzedAt":"2026-09-01T10:05:25.770Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T15:18:49.778Z"}