influxdata/influxdb · error · anyhow::Error
Plugin root has no parent directory
Error message
Plugin root has no parent directory
What it means
load_function_from_module treats plugin_root as a package directory whose parent is placed on sys.path; if the path has no parent (e.g. a bare relative component like Path('plugins') at filesystem root semantics, or an empty/root path), .parent() returns None and this anyhow error is raised. It means the plugin root path is malformed for module-style loading.
Solutions
- Pass an absolute path to the plugin directory, e.g. Path('/opt/influxdb3/plugins/my_plugin'), not just the directory name.
- Join the configured plugin base directory with the plugin name before calling: base_dir / plugin_name.
- Guard the path before loading: if not plugin_root.is_absolute(): plugin_root = base_dir / plugin_root.
Example fix
# before
root = Path(os.environ.get('PLUGIN_DIR', '')) # empty -> no parent
# after
root = (Path('/opt/influxdb3/plugins') / plugin_name).resolve() Defensive patterns
Strategy: validation
Validate before calling
from pathlib import Path
root = Path(plugin_name)
if not root.is_absolute():
root = (Path('/opt/influxdb3/plugins') / plugin_name).resolve() Try / catch
if not plugin_root.parent:
plugin_root = base_dir / plugin_root # normalize before calling the loader Prevention
- Always pass absolute, resolved paths for plugin directories.
- Join a configured base plugin directory with the plugin name instead of passing bare names.
- Validate plugin path config values at startup (non-empty, has parent, exists).
When it happens
Trigger: load_plugin_function given a plugin_root Path with no parent directory — e.g. a bare name like Path("my_plugin"), a root path like Path("/"), or an empty path — instead of a full directory path like /path/to/plugins/my_plugin.
Common situations: Passing just the plugin directory name read from config while the base directory wasn't joined; resolving plugin roots from env vars that are empty or '/'; incorrect working-directory assumptions making the path resolve to a bare component.
Understand the failure class
Background: "Invalid value" and "allowed values are" config errors: what your library rejected and how to fix it — this error's family across 41 libraries.
Related errors
- call site contains null bytes
- plugin code contains null bytes
- {0}
- CA certificate PEM should be valid
- Failed to list packages
AI-assisted analysis of influxdata/influxdb@06200ef96b (2026-09-19).
Data as JSON: /api/errors/d03f17c535f88764.
Report an issue: GitHub.
Appendix: source
Thrown at influxdb3_py_api/src/system_py.rs:329
.getattr("name")
.ok()
.and_then(|name| name.extract::<String>().ok())
.is_some_and(|name| name == module_name)
}
/// Loads a Python function from a multi-file plugin module.
///
/// This function temporarily adds the plugin's parent directory to Python's sys.path,
/// imports the module, retrieves the function, and then cleans up sys.path.
fn load_function_from_module<'py>(
py: Python<'py>,
plugin_root: &Path,
function_name: &str,
missing_fn_error: ExecutePluginError,
) -> Result<Bound<'py, PyAny>, ExecutePluginError> {
let parent_dir = plugin_root
.parent()
.ok_or_else(|| anyhow!("Plugin root has no parent directory"))?;
let module_name = plugin_root
.file_name()
.and_then(|n| n.to_str())
.ok_or_else(|| anyhow!("Invalid plugin directory name"))?;
let sys = py.import("sys").map_err(anyhow::Error::from)?;
let sys_path = sys.getattr("path").map_err(anyhow::Error::from)?;
let parent_dir_str = parent_dir
.to_str()
.ok_or_else(|| anyhow!("Invalid UTF-8 in parent directory path"))?;
// Acquire while attached to the Python runtime; `lock_py_attached` detaches
// internally if the lock is contended, avoiding a GIL/lock deadlock.
let _sys_path_guard = SYS_PATH_LOCK.lock_py_attached(py);
// Add parent directory to sys.path
sys_pathView on GitHub (pinned to 06200ef96b)