{"record":{"id":"e4bf357a8d2c72e6","repo":"p-e-w/heretic","slug":"plugin-file-plugin-path-does-not-exist","errorCode":null,"errorMessage":"Plugin file '{plugin_path}' does not exist","messagePattern":"Plugin file '(.+?)' does not exist","errorType":"exception","errorClass":"ImportError","httpStatus":null,"severity":"error","filePath":"src/heretic/plugin.py","lineNumber":103,"sourceCode":"        raise ValueError(\n            \"You must append the plugin class name to the filepath like this: path/to/plugin.py:ClassName\"\n        )\n\n    # File path with explicit class name, e.g. \"C:\\\\path\\\\plugin.py:MyPlugin\".\n    if \":\" in name:\n        file_path, class_name = name.rsplit(\":\", 1)\n        if not file_path.endswith(\".py\") or not class_name:\n            raise ValueError(\n                \"File-based plugin must use the form 'path/to/plugin.py:ClassName'\"\n            )\n\n        plugin_path = Path(file_path)\n        if not plugin_path.is_absolute():\n            plugin_path = Path.cwd() / plugin_path\n        plugin_path = plugin_path.resolve()\n\n        if not plugin_path.is_file():\n            raise ImportError(f\"Plugin file '{plugin_path}' does not exist\")\n\n        # We're writing directly to the sys.modules dict,\n        # so the typical restrictions on module names\n        # (no dots, slashes, etc.) don't apply.\n        module_name = f\"heretic_plugin_{plugin_path}\"\n\n        # Reuse already-loaded modules to avoid re-executing the plugin on repeated loads.\n        module = sys.modules.get(module_name)\n        if module is None:\n            spec = importlib.util.spec_from_file_location(module_name, plugin_path)\n            if spec is None or spec.loader is None:\n                raise ImportError(\n                    f\"Could not load plugin '{name}' (invalid module spec)\"\n                )\n\n            module = importlib.util.module_from_spec(spec)\n\n            # Cache before executing to match normal import semantics and allow","sourceCodeStart":85,"sourceCodeEnd":121,"githubUrl":"https://github.com/p-e-w/heretic/blob/bedb94ef117a271532ac2058447fbc165d5051bd/src/heretic/plugin.py#L85-L121","documentation":"The .py path portion of the plugin name (resolved to an absolute path via Path.cwd() if relative) does not exist on disk. load_plugin() raises ImportError before attempting any import so the user gets a clear missing-file message instead of a low-level import traceback.","triggerScenarios":"load_plugin('plugins/missing.py:MyScorer', ...) where plugins/missing.py does not exist; relative path given while running from a different working directory; typo in the path; file deleted or never committed.","commonSituations":"Running heretic from a different cwd than the config assumes (relative paths resolve against Path.cwd()); Docker containers without the plugin file mounted; renames/moves of the plugin file; case-sensitive filesystems (MyScorer.py vs myscorer.py on Linux).","solutions":["Check the resolved path printed in the message and fix the file location or the path in the config.","Use an absolute path to make the plugin independent of the working directory.","If relative, cd into the expected directory or fix your launcher's cwd.","Copy/mount the plugin file into the environment (e.g. Docker volume) where heretic runs."],"exampleFix":"# before\nscorer = \"scorers/keyword_rate.py:KeywordRate\"  # run from repo root only\n# after\nscorer = \"/opt/myproj/scorers/keyword_rate.py:KeywordRate\"","handlingStrategy":"validation","validationCode":"from pathlib import Path\ndef plugin_file_exists(name: str) -> bool:\n    p = Path(name.rsplit(\":\", 1)[0])\n    if not p.is_absolute():\n        p = Path.cwd() / p\n    return p.resolve().is_file()","typeGuard":null,"tryCatchPattern":"try:\n    cls = load_plugin(name, Scorer)\nexcept ImportError as e:\n    sys.exit(str(e))  # includes resolved missing path","preventionTips":["Prefer absolute paths for plugin files.","Run heretic from the directory your relative paths assume, or fix the cwd in your launcher.","Verify mounted volumes in containers include the plugin file."],"tags":["plugin","filesystem","configuration"],"backgroundTag":"file-not-found","analyzedSha":"bedb94ef117a271532ac2058447fbc165d5051bd","analyzedAt":"2026-08-29T08:38:06.692Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}