{"record":{"id":"3a8aaa2266c59d5a","repo":"pbakaus/impeccable","slug":"extension-manifest-json-expected-background-servi","errorCode":null,"errorMessage":"extension/manifest.json: expected background.service_worker to derive the Firefox manifest","messagePattern":"extension/manifest\\.json: expected background\\.service_worker to derive the Firefox manifest","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"scripts/build-extension.js","lineNumber":97,"sourceCode":"    `zip -r ${JSON.stringify(zipPath)} .${exArgs ? ' ' + exArgs : ''}`,\n    { cwd, stdio: 'pipe' },\n  );\n  const size = fs.statSync(zipPath).size;\n  console.log(`Packaged ${path.relative(ROOT, zipPath)} (${(size / 1024).toFixed(1)} KB)`);\n}\n\n// --- 3a. Chrome zip (manifest unchanged) ---\n\npackZip(path.join(DIST, 'extension.zip'), EXT_DIR, ['STORE_LISTING.md', '*.DS_Store']);\n\n// --- 3b. Firefox: derive a Gecko-compatible manifest and stage an unpacked\n// build (consumed by `web-ext lint` in CI), then zip it for AMO. ---\n\nconst chromeManifest = JSON.parse(fs.readFileSync(path.join(EXT_DIR, 'manifest.json'), 'utf-8'));\n\nconst serviceWorker = chromeManifest.background?.service_worker;\nif (!serviceWorker) {\n  throw new Error(\n    'extension/manifest.json: expected background.service_worker to derive the Firefox manifest',\n  );\n}\n\nconst firefoxManifest = {\n  ...chromeManifest,\n  // Gecko supports MV3 via non-persistent event pages. Declaring `scripts`\n  // (rather than `service_worker`) is the path supported across all MV3 Firefox\n  // releases; service-worker.js uses only top-level listeners + an in-memory\n  // Map, so it runs unchanged as an event page.\n  background: { scripts: [serviceWorker] },\n  // Required by AMO for signing/distribution. Ignored by Chrome.\n  browser_specific_settings: {\n    gecko: {\n      id: 'impeccable@bakaus.com',\n      // `data_collection_permissions` (below) is required by AMO for new\n      // submissions and is only honored on Firefox 140+. We set the floor to\n      // 140 so the declared min version actually supports every key we ship;","sourceCodeStart":79,"sourceCodeEnd":115,"githubUrl":"https://github.com/pbakaus/impeccable/blob/d14711ae3d1a1dd62dee61a358d27f107c51ccd0/scripts/build-extension.js#L79-L115","documentation":"Thrown by the Firefox-extension build step when the Chrome extension manifest lacks background.service_worker. The Firefox (AMO) manifest is DERIVED from the Chrome manifest by converting the service worker into an event-page script, so a missing service_worker means the transform has no source value and cannot proceed.","triggerScenarios":"Running scripts/build-extension.js when extension/manifest.json has no top-level background object, or has background.scripts instead of background.service_worker, or the MV3 service worker key was removed/renamed during a manifest edit.","commonSituations":"Refactoring the extension to a different background model (e.g. switches to background.scripts for MV2 compat) without updating the Firefox derivation; a manifest rewrite that dropped the background block; building the extension from a stale checkout after the manifest schema changed.","solutions":["Open extension/manifest.json and confirm `background.service_worker` (e.g. \"background.service_worker\": \"service-worker.js\") is present and points at the worker file.","If you intentionally moved off a service worker, update scripts/build-extension.js to derive the Firefox background block from whatever source shape you now use.","Re-run `bun run build:extension` after fixing the manifest."],"exampleFix":"// before — extension/manifest.json\n{\n  \"manifest_version\": 3,\n  \"background\": {}\n}\n\n// after\n{\n  \"manifest_version\": 3,\n  \"background\": { \"service_worker\": \"service-worker.js\" }\n}","handlingStrategy":"validation","validationCode":"const manifest = JSON.parse(fs.readFileSync(path.join(EXT_DIR, 'manifest.json'), 'utf-8'));\nif (!manifest.background?.service_worker) {\n  throw new Error('Add background.service_worker to extension/manifest.json before building Firefox.');\n}","typeGuard":"function hasServiceWorker(manifest) {\n  return typeof manifest?.background?.service_worker === 'string' && manifest.background.service_worker.length > 0;\n}","tryCatchPattern":"try {\n  buildFirefoxExtension();\n} catch (err) {\n  if (/background.service_worker/.test(err.message)) {\n    console.error('Firefox build needs background.service_worker in the Chrome manifest.');\n  } else throw err;\n}","preventionTips":["Validate the manifest shape in CI before the extension build step.","Keep the Chrome manifest's background.service_worker as the single source the Firefox derivation reads.","Add a manifest schema check (jsonschema) to catch regressions from edits."],"tags":["extension","manifest","firefox","build","mv3","config"],"backgroundTag":null,"analyzedSha":"d14711ae3d1a1dd62dee61a358d27f107c51ccd0","analyzedAt":"2026-08-13T00:52:25.771Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}