{"record":{"id":"14ba80ff275c101d","repo":"p-e-w/heretic","slug":"error-loading-plugin-name-e","errorCode":null,"errorMessage":"Error loading plugin '{name}': {e}","messagePattern":"Error loading plugin '(.+?)': (.+?)","errorType":"exception","errorClass":"ImportError","httpStatus":null,"severity":"error","filePath":"src/heretic/plugin.py","lineNumber":141,"sourceCode":"            sys.modules[module_name] = module\n            try:\n                spec.loader.exec_module(module)\n            except Exception:\n                sys.modules.pop(module_name, None)\n                raise\n\n        plugin_cls = validate_class(module, class_name)\n    # Fully-qualified import path, e.g \"heretic.scorers.keyword_rate.KeywordRate\".\n    else:\n        if \".\" not in name:\n            raise ValueError(\n                \"Import-based plugin must use the form 'fully.qualified.module.ClassName'\"\n            )\n        module_name, class_name = name.rsplit(\".\", 1)\n        try:\n            module = importlib.import_module(module_name)\n        except ImportError as e:\n            raise ImportError(f\"Error loading plugin '{name}': {e}\") from e\n        plugin_cls = validate_class(module, class_name)\n\n    if not issubclass(plugin_cls, base_class):\n        raise TypeError(f\"Plugin '{name}' must subclass {base_class.__name__}\")\n\n    return plugin_cls\n\n\nclass Context:\n    \"\"\"\n    Runtime context passed to plugins\n\n    Provides plugin-safe access to the model.\n\n    Plugins must use `get_responses(...)`, `get_logits(...)`, etc.\n    Direct access to the underlying Model is intentionally not exposed.\n    \"\"\"\n","sourceCodeStart":123,"sourceCodeEnd":159,"githubUrl":"https://github.com/p-e-w/heretic/blob/bedb94ef117a271532ac2058447fbc165d5051bd/src/heretic/plugin.py#L123-L159","documentation":"importlib.import_module() raised ImportError while importing the module part of an import-based plugin name; load_plugin() re-raises it wrapped with the original plugin name and chains the underlying exception (raise ... from e) so the root cause stays visible.","triggerScenarios":"load_plugin('mypkg.scorers.mine.MyScorer', ...) where mypkg is not installed or not on sys.path; the module itself raises ImportError at import time (missing dependency inside the plugin); a typo in a module segment.","commonSituations":"Plugin package not pip-installed into the active venv; running under a different Python/venv than where the plugin is installed; plugin imports a third-party library that is missing; PYTHONPATH not set in the deployment environment.","solutions":["Read the chained 'from e' cause at the bottom of the traceback for the real reason.","pip install the plugin package (or set PYTHONPATH) in the same environment heretic runs in.","If a dependency inside the plugin is missing, install that dependency.","Switch to the file-based form 'path/to/plugin.py:ClassName' if you do not want to install the package."],"exampleFix":"# before\n$ heretic --scorer mypkg.scorers.Mine.MyScorer   # ModuleNotFoundError: mypkg\n# after\n$ pip install -e ./mypkg\n$ heretic --scorer mypkg.scorers.mine.MyScorer","handlingStrategy":"try-catch","validationCode":"import importlib.util\ndef module_importable(dotted: str) -> bool:\n    mod = dotted.rsplit(\".\", 1)[0]\n    return importlib.util.find_spec(mod) is not None","typeGuard":null,"tryCatchPattern":"try:\n    cls = load_plugin(name, Scorer)\nexcept ImportError as e:\n    logging.exception(\"plugin '%s' could not be imported\", name)  # chained cause included\n    sys.exit(1)","preventionTips":["pip install plugin packages into the same venv heretic uses.","Set PYTHONPATH in deployment scripts/containers.","Import plugin dependencies at module top so missing deps surface early with a clear cause."],"tags":["plugin","python","import","module-not-found"],"backgroundTag":"module-not-found","analyzedSha":"bedb94ef117a271532ac2058447fbc165d5051bd","analyzedAt":"2026-08-29T08:38:06.692Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}