pbakaus/impeccable · error
extension/manifest.json: expected background.service_worker
Error message
extension/manifest.json: expected background.service_worker to derive the Firefox manifest
What it means
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.
Source
Thrown at scripts/build-extension.js:97
`zip -r ${JSON.stringify(zipPath)} .${exArgs ? ' ' + exArgs : ''}`,
{ cwd, stdio: 'pipe' },
);
const size = fs.statSync(zipPath).size;
console.log(`Packaged ${path.relative(ROOT, zipPath)} (${(size / 1024).toFixed(1)} KB)`);
}
// --- 3a. Chrome zip (manifest unchanged) ---
packZip(path.join(DIST, 'extension.zip'), EXT_DIR, ['STORE_LISTING.md', '*.DS_Store']);
// --- 3b. Firefox: derive a Gecko-compatible manifest and stage an unpacked
// build (consumed by `web-ext lint` in CI), then zip it for AMO. ---
const chromeManifest = JSON.parse(fs.readFileSync(path.join(EXT_DIR, 'manifest.json'), 'utf-8'));
const serviceWorker = chromeManifest.background?.service_worker;
if (!serviceWorker) {
throw new Error(
'extension/manifest.json: expected background.service_worker to derive the Firefox manifest',
);
}
const firefoxManifest = {
...chromeManifest,
// Gecko supports MV3 via non-persistent event pages. Declaring `scripts`
// (rather than `service_worker`) is the path supported across all MV3 Firefox
// releases; service-worker.js uses only top-level listeners + an in-memory
// Map, so it runs unchanged as an event page.
background: { scripts: [serviceWorker] },
// Required by AMO for signing/distribution. Ignored by Chrome.
browser_specific_settings: {
gecko: {
id: 'impeccable@bakaus.com',
// `data_collection_permissions` (below) is required by AMO for new
// submissions and is only honored on Firefox 140+. We set the floor to
// 140 so the declared min version actually supports every key we ship;View on GitHub (pinned to d14711ae3d)
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.
Example fix
// before — extension/manifest.json
{
"manifest_version": 3,
"background": {}
}
// after
{
"manifest_version": 3,
"background": { "service_worker": "service-worker.js" }
} Defensive patterns
Strategy: validation
Validate before calling
const manifest = JSON.parse(fs.readFileSync(path.join(EXT_DIR, 'manifest.json'), 'utf-8'));
if (!manifest.background?.service_worker) {
throw new Error('Add background.service_worker to extension/manifest.json before building Firefox.');
} Type guard
function hasServiceWorker(manifest) {
return typeof manifest?.background?.service_worker === 'string' && manifest.background.service_worker.length > 0;
} Try / catch
try {
buildFirefoxExtension();
} catch (err) {
if (/background.service_worker/.test(err.message)) {
console.error('Firefox build needs background.service_worker in the Chrome manifest.');
} else throw err;
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- invalid catalog: ${validation.errors.join('; ')}
- Could not extract browser antipattern registry
- Cannot build OpenAI plugin: missing ${label}: ${absPath}
- Cannot create ${zipFileName}: provider directory not found:
- Created ${zipFileName} but it contains no entries (source: $
AI-assisted analysis of pbakaus/impeccable@d14711ae3d (2026-08-13).
Data as JSON: /api/errors/3a8aaa2266c59d5a.
Report an issue: GitHub.