{"record":{"id":"b600a6a8b6e54d4f","repo":"gildas-lormeau/SingleFile","slug":"singlefile-capture-config-must-be-an-object","errorCode":null,"errorMessage":"SingleFile capture config must be an object","messagePattern":"SingleFile capture config must be an object","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/core/bg/external-messages.js","lineNumber":150,"sourceCode":"\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}\n\nfunction getCaptureConfig(message) {\n\tconst { config } = message;\n\tif (config == null) {\n\t\treturn {};\n\t}\n\tif (typeof config != \"object\" || Array.isArray(config)) {\n\t\tthrow new Error(\"SingleFile capture config must be an object\");\n\t}\n\tconst captureConfig = {};\n\tCAPTURE_OPTION_NAMES.forEach(optionName => {\n\t\tif (config[optionName] !== undefined) {\n\t\t\tcaptureConfig[optionName] = config[optionName];\n\t\t}\n\t});\n\treturn captureConfig;\n}\n\nasync function queryTabs(options) {\n\tconst tabs = await browser.tabs.query(options);\n\treturn tabs.sort((tab1, tab2) => tab1.index - tab2.index);\n}\n","sourceCodeStart":132,"sourceCodeEnd":165,"githubUrl":"https://github.com/gildas-lormeau/SingleFile/blob/517fb7c5cf2096d89933b747e862d8ecf616a9f9/src/core/bg/external-messages.js#L132-L165","documentation":"getCaptureConfig validates the optional 'config' field of an external capture message. null/undefined is allowed (defaults to {}), but any non-object value — including arrays — is rejected with 'SingleFile capture config must be an object'. This ensures capture options are a plain map of option names to values.","triggerScenarios":"Sending a capture message where message.config is a string, number, boolean, or an Array (arrays are explicitly rejected even though typeof Array === 'object').","commonSituations":"Callers JSON-stringify their config and forget to parse it; callers pass a list of option names instead of an options map; dynamic config built from query params stays a string.","solutions":["Pass config as a plain object keyed by capture option names, e.g. { backgroundSave: true }.","Parse JSON strings before sending: config = JSON.parse(configStr).","Convert arrays into an object map before sending.","Omit the config field entirely if you want defaults."],"exampleFix":"// before\nsendMessage({ method: 'capture', config: ['backgroundSave', 'removeHiddenElements'] });\n// after\nsendMessage({ method: 'capture', config: { backgroundSave: true, removeHiddenElements: true } });","handlingStrategy":"validation","validationCode":"function isValidCaptureConfig(config) {\n  return config == null || (typeof config === 'object' && !Array.isArray(config));\n}\nif (!isValidCaptureConfig(myConfig)) throw new TypeError('config must be a plain object');","typeGuard":"function isPlainObject(v) {\n  return v !== null && typeof v === 'object' && !Array.isArray(v);\n}","tryCatchPattern":"if (!isPlainObject(config) && config != null) {\n  config = {}; // or normalize/parse before sending\n}\nawait sendMessage({ method: 'capture', config });","preventionTips":["Always send config as a plain object, never arrays or JSON strings","Parse JSON strings before assigning to message.config","Omit config entirely when you want defaults"],"tags":["validation","type-error","extension-messaging"],"backgroundTag":"schema-validation-failed","analyzedSha":"517fb7c5cf2096d89933b747e862d8ecf616a9f9","analyzedAt":"2026-09-01T10:05:25.770Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T15:18:49.778Z"}