influxdata/influxdb · error · ExecutePluginError

Failed to load function '{}' from plugin module '{}': {}

Error message

Failed to load function '{}' from plugin module '{}': {}

What it means

The InfluxDB 3 Python plugin host imports each plugin module and resolves the configured entry-point function (e.g. process_request for request plugins) via getattr. A plain AttributeError is mapped to a dedicated MissingProcessRequestFunction error; this PluginError is only produced when the attribute lookup fails with some other exception. The message shows the requested function name, module name, and the underlying Python exception, which is the real cause.

Source

Thrown at influxdb3_py_api/src/system_py.rs:387

    let _ = sys_path.call_method1("pop", (0,));

    // Any import failure is the real load error and must not be masked.
    let module = import_result.map_err(|e| {
        ExecutePluginError::PluginError(anyhow!(
            "Failed to import plugin module '{}': {}\n\
             Hint: Check for syntax errors or missing dependencies in Python files.",
            module_name,
            e
        ))
    })?;

    module.getattr(function_name).map_err(|e| {
        // Only a missing attribute on the imported module means the entry-point
        // function is absent; surface anything else as the real cause.
        if e.is_instance_of::<PyAttributeError>(py) {
            missing_fn_error
        } else {
            ExecutePluginError::PluginError(anyhow!(
                "Failed to load function '{}' from plugin module '{}': {}",
                function_name,
                module_name,
                e
            ))
        }
    })
}

/// Execute a WAL flush trigger plugin.
#[allow(clippy::too_many_arguments)]
pub fn execute_wal_flush_trigger(
    code: &str,
    wal_data: &[WalFlushElement],
    schema: Arc<DatabaseSchema>,
    query_endpoint: Arc<dyn QueryEndpoint>,
    write_endpoint: Arc<dyn WriteEndpoint>,
    logger: PluginLogger,

View on GitHub (pinned to d28e26e048)

Solutions

  1. Read the trailing Python exception in the message - it names the actual failing code in the plugin module
  2. Reproduce outside the server: run python -c "import my_plugin; my_plugin.process_request" from the plugin directory
  3. Make the entry point a plain module-level def and remove module-level __getattr__/descriptors that intercept the lookup
  4. Delete stale __pycache__ directories under the plugin root and reload the plugin

Example fix

# before (plugin.py)
_ENTRY_POINTS = {}
def __getattr__(name):
    if name not in _ENTRY_POINTS:
        raise RuntimeError(f'unknown entry point: {name}')  # becomes PluginError
    return _ENTRY_POINTS[name]

# after
def process_request(influxdb, query_params, request_params, body, args):
    ...
Defensive patterns

Strategy: validation

Validate before calling

# preflight the plugin before deploying it
import importlib
mod = importlib.import_module('my_plugin')
try:
    fn = getattr(mod, 'process_request')
except AttributeError:
    raise SystemExit('entry point missing')
except Exception as e:
    raise SystemExit(f'entry-point lookup itself raised: {e}')  # this is the PluginError path
assert callable(fn)

Type guard

def entry_point_ok(module_name: str, fn_name: str = 'process_request') -> bool:
    try:
        return callable(getattr(importlib.import_module(module_name), fn_name))
    except Exception:
        return False

Try / catch

// Rust: distinguish load failure from missing function
match execute_plugin(...) {
    Err(ExecutePluginError::PluginError(e)) => log::error!("plugin load failed: {e:#}"),
    Err(ExecutePluginError::MissingProcessRequestFunction) => log::error!("process_request not defined"),
    Ok(v) => { /* ... */ }
}

Prevention

When it happens

Trigger: The plugin module defines a module-level __getattr__ (PEP 562) that raises for the entry-point name; the entry point is a descriptor/property whose access raises; or a package __init__ was left partially initialized after a swallowed import error, so getattr(process_request) raises something other than AttributeError.

Common situations: Plugins using metaprogramming (lazy imports, generated attributes), plugin code written against a newer/older SDK whose module shape changed, or stale __pycache__ from a different Python version causing attribute access to fail.

Related errors


AI-assisted analysis of influxdata/influxdb@d28e26e048 (2026-08-16). Data as JSON: /api/errors/3346010afe1c8410. Report an issue: GitHub.