influxdata/influxdb · error · ExecutePluginError::PluginError

Failed to load function

Error message

Failed to load function '{function_name}' from plugin module '{module_name}': {e}

What it means

Thrown when the plugin module imported successfully but `module.getattr(function_name)` fails with something other than a plain missing attribute (e.g. AttributeError whose getter raised, TypeError, or another Python exception during attribute access). A plain missing function yields a distinct 'missing function' error; this error surfaces all other getattr failures with their original cause.

Solutions

  1. Inspect the wrapped Python exception text — it shows why attribute access failed; fix that in the plugin file.
  2. Confirm the entry-point function is defined at module top level with the exact name passed to load_plugin_function: `def _trigger(...)` matching the configured name.
  3. Remove any custom __getattr__/module-level magic in the plugin; define the function directly.
  4. Simplify the plugin to a minimal `def trigger(...): pass` and reload to isolate whether the failure is in the function definition chain.

Example fix

// before (plugin my_plugin.py)
class Wrapper:
    def __getattr__(self, name):
        raise RuntimeError("boom")
trigger = Wrapper()
// after
def trigger(args):
    ...
Defensive patterns

Strategy: try-catch

Validate before calling

// verify the entry point exists before triggering a load
import subprocess, sys, json
r = subprocess.run([sys.executable, "-c",
  f"import {module_name}; print(hasattr({module_name}, '{fn_name}'))"],
  capture_output=True, text=True, cwd=plugin_parent_dir)
if r.stdout.strip() != "True":
    raise SystemExit(f"Function '{fn_name}' not found in plugin '{module_name}'")

Try / catch

try {
  loadPluginFunction(dir, module, fn);
} catch (e) {
  if (String(e.message).includes("Failed to load function")) {
    console.error(`Entry point '${fn}' inaccessible in '${module}': ${e.message}`);
  } else throw e;
}

Prevention

When it happens

Trigger: load_plugin_function asks for a trigger function name that exists as an attribute whose access raises — e.g. the module defines `function_name` as a property/descriptor that raises, name is shadowed by an object whose __getattr__ throws, or a module-level __getattr__ hook raises instead of raising AttributeError cleanly.

Common situations: Plugin defines the trigger name but assignment itself failed silently earlier; module uses `__getattr__` lazy loading that throws; a typo'd decorator wraps the function in an object that errors on access; Python module partially executed after a caught exception left names unbound.

Related errors


AI-assisted analysis of influxdata/influxdb@06200ef96b (2026-09-19). Data as JSON: /api/errors/478a094a6cddea80. Report an issue: GitHub.

Appendix: source

Thrown at influxdb3_py_api/src/system_py.rs:383

    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.
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,
    table_filter: Option<TableId>,

View on GitHub (pinned to 06200ef96b)