{"record":{"id":"872b3d34c03cd12e","repo":"SeleniumHQ/selenium","slug":"add-on-path-is-neither-an-xpi-nor-a-directory-ad","errorCode":null,"errorMessage":"Add-on path is neither an XPI nor a directory: {addon_path}","messagePattern":"Add-on path is neither an XPI nor a directory: (.+?)","errorType":"exception","errorClass":"OSError","httpStatus":null,"severity":"error","filePath":"py/selenium/webdriver/firefox/firefox_profile.py","lineNumber":293,"sourceCode":"            raise OSError(f\"Add-on path does not exist: {addon_path}\")\n\n        try:\n            if zipfile.is_zipfile(addon_path):\n                with zipfile.ZipFile(addon_path, \"r\") as compressed_file:\n                    if \"manifest.json\" in compressed_file.namelist():\n                        return parse_manifest_json(compressed_file.read(\"manifest.json\"))\n\n                    manifest = compressed_file.read(\"install.rdf\")\n            elif os.path.isdir(addon_path):\n                manifest_json_filename = os.path.join(addon_path, \"manifest.json\")\n                if os.path.exists(manifest_json_filename):\n                    with open(manifest_json_filename, encoding=\"utf-8\") as f:\n                        return parse_manifest_json(f.read())\n\n                with open(os.path.join(addon_path, \"install.rdf\"), encoding=\"utf-8\") as f:\n                    manifest = f.read()\n            else:\n                raise OSError(f\"Add-on path is neither an XPI nor a directory: {addon_path}\")\n        except (OSError, KeyError) as e:\n            raise AddonFormatError(str(e), sys.exc_info()[2])\n\n        try:\n            doc = minidom.parseString(manifest)\n\n            # Get the namespaces abbreviations\n            em = get_namespace_id(doc, \"http://www.mozilla.org/2004/em-rdf#\")\n            rdf = get_namespace_id(doc, \"http://www.w3.org/1999/02/22-rdf-syntax-ns#\")\n\n            description = doc.getElementsByTagName(rdf + \"Description\").item(0)\n            if not description:\n                description = doc.getElementsByTagName(\"Description\").item(0)\n            for node in description.childNodes:\n                # Remove the namespace prefix from the tag for comparison\n                entry = node.nodeName.replace(em, \"\")\n                if entry in details:\n                    details.update({entry: get_text(node)})","sourceCodeStart":275,"sourceCodeEnd":311,"githubUrl":"https://github.com/SeleniumHQ/selenium/blob/aa36b38e696a0909e973bdf5e2f9031ffe842c4b/py/selenium/webdriver/firefox/firefox_profile.py#L275-L311","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Confirm the file is a valid zip: run zipfile.is_zipfile(addon_path) to check before calling","Re-download the .xpi if it may be corrupted or truncated","If installing from a directory, ensure the path is a directory and not a single file"],"exampleFix":"// before\nprofile.add_extension('/downloads/plugin')  # 'plugin' is a plain file\n\n// after\nimport zipfile\nif zipfile.is_zipfile('/downloads/plugin.xpi'):\n    profile.add_extension('/downloads/plugin.xpi')\nelse:\n    raise ValueError('Not a valid XPI archive')","handlingStrategy":"validation","validationCode":"import os, zipfile\naddon_path = '/path/to/addon'\nif not (zipfile.is_zipfile(addon_path) or os.path.isdir(addon_path)):\n    raise ValueError(f'Path must be an XPI or directory: {addon_path}')\nprofile.add_extension(addon_path)","typeGuard":"import os, zipfile\ndef is_valid_addon_path(p: str) -> bool:\n    return os.path.exists(p) and (zipfile.is_zipfile(p) or os.path.isdir(p))","tryCatchPattern":"from selenium.common.exceptions import AddonFormatError\ntry:\n    profile.add_extension(addon_path)\nexcept (OSError, AddonFormatError) as e:\n    print(f'Invalid addon: {e}')\n    # re-download or use alternate path","preventionTips":["Verify the file is a valid zip with zipfile.is_zipfile before passing","Ensure the path points to a directory or .xpi, not a plain file","Re-download corrupted .xpi files"],"tags":["firefox","addon","file-format","zip","profile"],"backgroundTag":null,"analyzedSha":"aa36b38e696a0909e973bdf5e2f9031ffe842c4b","analyzedAt":"2026-08-14T02:32:32.244Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}