SeleniumHQ/selenium · error · OSError

Add-on path is neither an XPI nor a directory: {addon_path}

Error message

Add-on path is neither an XPI nor a directory: {addon_path}

What it means

Raised when the addon_path exists but is neither a valid ZIP/XPI file (zipfile.is_zipfile returns False) nor a directory (os.path.isdir returns False). This catches cases like passing a regular file, a broken archive, or a symlink to a non-directory.

Source

Thrown at py/selenium/webdriver/firefox/firefox_profile.py:293

            raise OSError(f"Add-on path does not exist: {addon_path}")

        try:
            if zipfile.is_zipfile(addon_path):
                with zipfile.ZipFile(addon_path, "r") as compressed_file:
                    if "manifest.json" in compressed_file.namelist():
                        return parse_manifest_json(compressed_file.read("manifest.json"))

                    manifest = compressed_file.read("install.rdf")
            elif os.path.isdir(addon_path):
                manifest_json_filename = os.path.join(addon_path, "manifest.json")
                if os.path.exists(manifest_json_filename):
                    with open(manifest_json_filename, encoding="utf-8") as f:
                        return parse_manifest_json(f.read())

                with open(os.path.join(addon_path, "install.rdf"), encoding="utf-8") as f:
                    manifest = f.read()
            else:
                raise OSError(f"Add-on path is neither an XPI nor a directory: {addon_path}")
        except (OSError, KeyError) as e:
            raise AddonFormatError(str(e), sys.exc_info()[2])

        try:
            doc = minidom.parseString(manifest)

            # Get the namespaces abbreviations
            em = get_namespace_id(doc, "http://www.mozilla.org/2004/em-rdf#")
            rdf = get_namespace_id(doc, "http://www.w3.org/1999/02/22-rdf-syntax-ns#")

            description = doc.getElementsByTagName(rdf + "Description").item(0)
            if not description:
                description = doc.getElementsByTagName("Description").item(0)
            for node in description.childNodes:
                # Remove the namespace prefix from the tag for comparison
                entry = node.nodeName.replace(em, "")
                if entry in details:
                    details.update({entry: get_text(node)})

View on GitHub (pinned to aa36b38e69)

Solutions

  1. Confirm the file is a valid zip: run zipfile.is_zipfile(addon_path) to check before calling
  2. Re-download the .xpi if it may be corrupted or truncated
  3. If installing from a directory, ensure the path is a directory and not a single file

Example fix

// before
profile.add_extension('/downloads/plugin')  # 'plugin' is a plain file

// after
import zipfile
if zipfile.is_zipfile('/downloads/plugin.xpi'):
    profile.add_extension('/downloads/plugin.xpi')
else:
    raise ValueError('Not a valid XPI archive')
Defensive patterns

Strategy: validation

Validate before calling

import os, zipfile
addon_path = '/path/to/addon'
if not (zipfile.is_zipfile(addon_path) or os.path.isdir(addon_path)):
    raise ValueError(f'Path must be an XPI or directory: {addon_path}')
profile.add_extension(addon_path)

Type guard

import os, zipfile
def is_valid_addon_path(p: str) -> bool:
    return os.path.exists(p) and (zipfile.is_zipfile(p) or os.path.isdir(p))

Try / catch

from selenium.common.exceptions import AddonFormatError
try:
    profile.add_extension(addon_path)
except (OSError, AddonFormatError) as e:
    print(f'Invalid addon: {e}')
    # re-download or use alternate path

Prevention

When it happens

Trigger: Passing a path to a plain file (e.g., a .txt or .json), a corrupted/partially-downloaded .xpi that is no longer a valid zip, or a path that points to a named pipe or special file.

Common situations: Corrupted downloads where the .xpi is incomplete or contains HTML error content. Accidentally pointing at a README or metadata file instead of the actual addon package.

Related errors


AI-assisted analysis of SeleniumHQ/selenium@aa36b38e69 (2026-08-14). Data as JSON: /api/errors/872b3d34c03cd12e. Report an issue: GitHub.