browser-use/browser-use · error · Exception

No manifest.json found in extension

Error message

No manifest.json found in extension

What it means

_extract_extension unzipped the downloaded file successfully but found no manifest.json at the archive root — every real Chrome extension must ship one. Usually means the URL served an HTML error page or a ZIP whose manifest sits in a subdirectory rather than a direct .crx.

Source

Thrown at browser_use/browser/profile.py:1201

		import os
		import zipfile

		# Remove existing directory
		if extract_dir.exists():
			import shutil

			shutil.rmtree(extract_dir)

		extract_dir.mkdir(parents=True, exist_ok=True)

		try:
			# CRX files are ZIP files with a header, try to extract as ZIP
			with zipfile.ZipFile(crx_path, 'r') as zip_ref:
				zip_ref.extractall(extract_dir)

			# Verify manifest exists
			if not (extract_dir / 'manifest.json').exists():
				raise Exception('No manifest.json found in extension')

		except zipfile.BadZipFile:
			# CRX files have a header before the ZIP data
			# Skip the CRX header and extract the ZIP part
			with open(crx_path, 'rb') as f:
				# Read CRX header to find ZIP start
				magic = f.read(4)
				if magic != b'Cr24':
					raise Exception('Invalid CRX file format')

				version = int.from_bytes(f.read(4), 'little')
				if version == 2:
					pubkey_len = int.from_bytes(f.read(4), 'little')
					sig_len = int.from_bytes(f.read(4), 'little')
					f.seek(16 + pubkey_len + sig_len)  # Skip to ZIP data
				elif version == 3:
					header_len = int.from_bytes(f.read(4), 'little')
					f.seek(12 + header_len)  # Skip to ZIP data

View on GitHub (pinned to 6c73fced2f)

Solutions

  1. Inspect the downloaded artifact (unzip -l) — if you see HTML files or a nested folder, the URL is wrong.
  2. Use the direct .crx asset URL (e.g. the GitHub release .crx file or a direct CDN link), not a page URL.
  3. For nested ZIPs, download and extract manually, then point extensions at the directory containing manifest.json.
  4. For repos, pack the extension yourself (zip manifest.json + sources) or reference a subdirectory path locally.

Example fix

# before
profile = BrowserProfile(extensions=[{'url': 'https://github.com/foo/bar/archive/refs/heads/main.zip'}])  # manifest nested

# after
# download, unzip, then load the folder containing manifest.json:
profile = BrowserProfile(extensions=[{'path': '/opt/ext/bar-main/extension'}])
Defensive patterns

Strategy: validation

Validate before calling

import zipfile

def zip_has_root_manifest(path: str) -> bool:
    with zipfile.ZipFile(path) as z:
        return 'manifest.json' in z.namelist()

# verify the artifact before wiring it into BrowserProfile

Try / catch

try:
    profile = BrowserProfile(extensions=[{'url': ext_url}])
except Exception as e:
    if 'No manifest.json' in str(e):
        # download + extract manually, locate manifest.json, use directory form
        profile = BrowserProfile(extensions=[{'path': found_ext_dir}])
    else:
        raise

Prevention

When it happens

Trigger: extensions url points to a Chrome Web Store page or a redirect that returns HTML zipped/renamed; a GitHub release asset that is a source ZIP with the extension nested one folder deep; a corrupted download.

Common situations: Pasting the Web Store listing URL instead of the direct .crx download URL; GitHub 'Download ZIP' of a repo where manifest.json is inside <repo>-main/; truncated downloads.

Related errors


AI-assisted analysis of browser-use/browser-use@6c73fced2f (2026-08-14). Data as JSON: /api/errors/af81cd7b5566d336. Report an issue: GitHub.