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 dataView on GitHub (pinned to 6c73fced2f)
Solutions
- Inspect the downloaded artifact (unzip -l) — if you see HTML files or a nested folder, the URL is wrong.
- Use the direct .crx asset URL (e.g. the GitHub release .crx file or a direct CDN link), not a page URL.
- For nested ZIPs, download and extract manually, then point extensions at the directory containing manifest.json.
- 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
- Use direct .crx download URLs, never Web Store page URLs or repo source ZIPs.
- Verify artifacts once in CI (unzip -l | grep manifest.json) and cache them.
- For GitHub projects, reference the release .crx asset or ship an unpacked dir.
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.