influxdata/influxdb · error · anyhow::Error
Invalid plugin directory name
Error message
Invalid plugin directory name
What it means
Thrown by load_function_from_module in influxdb3_py_api when loading a Python plugin from a directory. The plugin root path's final component (the directory name, used as the Python module name) cannot be converted to a UTF-8 string via Path::file_name().to_str(), typically because the path is malformed (e.g. ends in '..' or is a bare root) or contains non-UTF-8 bytes.
Solutions
- Rename the plugin directory so its name is plain ASCII/UTF-8 and contains no '..' or trailing separators.
- Normalize the plugin_directory value before passing it (strip trailing '/', resolve '.'/'..' with std::path::Path::normalize/canonicalize).
- Verify where the path comes from (config file, env var) and ensure it is valid UTF-8: `echo "$PLUGIN_DIR" | iconv -f utf-8 -t utf-8` or `PathBuf::into_os_string().into_string()` in Rust to validate early.
- If loading fails at startup, list the plugins directory (`ls`) to spot oddly named directories.
Example fix
// before
let dir = std::env::var("PLUGIN_DIR").unwrap(); // may contain trailing slash or non-UTF-8
load_plugin_function(&dir, ...);
// after
let dir = std::env::var("PLUGIN_DIR").unwrap();
let dir = std::path::Path::new(&dir).canonicalize().unwrap(); // resolves '..', strips separators, validates
load_plugin_function(&dir.to_str().expect("plugin dir must be UTF-8"), ...); Defensive patterns
Strategy: validation
Validate before calling
const plugin_dir = "/var/lib/influxdb3/plugins/my_plugin";
const name = plugin_dir.split("/").filter(Boolean).pop() ?? "";
if (!name || !/^\w[\w.-]*$/.test(name) || name === ".." || name === ".") {
throw new Error(`Invalid plugin directory name: '${name}'`);
} Type guard
function isValidPluginDirName(p: string): boolean {
const name = p.split("/").filter(Boolean).pop() ?? "";
return name.length > 0 && name !== "." && name !== ".." && /^\w[\w.-]*$/.test(name);
} Prevention
- Create plugin directories with simple ASCII names via mkdir, not scripted byte-level names.
- Canonicalize/clean plugin paths before passing them to the loader.
- Never pass paths with trailing slashes or '..' segments to load_plugin_function.
When it happens
Trigger: Calling load_plugin_function with a plugin_directory whose final component is not valid UTF-8 (non-UTF-8 bytes from filesystem encoding), or a path where file_name() resolves oddly (trailing path separators, root paths like '/', or '..' components).
Common situations: Plugin directories created with shell-generated names containing raw bytes; paths passed with trailing slashes ('/plugins/my_plugin/') or dot components; plugin dir supplied from an env var or config built with non-UTF-8 locale filesystem encoding (rare on Linux, possible on some systems).
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- Invalid UTF-8 in parent directory path
- Error converting CSV output to UTF-8
- Error converting JSON output to UTF-8
- error converting the schedule time
- error writing wal file
AI-assisted analysis of influxdata/influxdb@06200ef96b (2026-09-19).
Data as JSON: /api/errors/720a7de8bad760f1.
Report an issue: GitHub.
Appendix: source
Thrown at influxdb3_py_api/src/system_py.rs:333
}
/// 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_path
.call_method1("insert", (0, parent_dir_str))
.map_err(anyhow::Error::from)?;
// A plugin dir added after FileFinder cached the parent is invisible untilView on GitHub (pinned to 06200ef96b)