immich-app/immich · error
Plugin method call failed
Error message
Plugin method call failed: ${label}#${methodName} What it means
callMethod acquires a worker from the plugin pool, calls the requested method, and wraps any failure (plugin throws, worker crashes, serialization error, timeout) in 'Plugin method call failed: <label>#<methodName>' with the cause preserved. The plugin itself remains loaded; only the method invocation failed.
Solutions
- Inspect the `cause` for the actual error thrown inside the plugin method
- Validate/serialize the input payload before the call (ensure JSON.stringify works and shape matches the method contract)
- Check worker logs/health for crashes (OOM, unhandled rejection) at the time of the call
- Reproduce the method call with the same input directly against the plugin to debug
Example fix
// before
const r = await pluginRepo.callMethod({ pluginKey: 'geo-plugin', methodName: 'geocode' }, circularObj);
// after
const r = await pluginRepo.callMethod({ pluginKey: 'geo-plugin', methodName: 'geocode' }, JSON.parse(JSON.stringify(sanitizedInput))); Defensive patterns
Strategy: try-catch
Validate before calling
// guard input serializability and shape before calling const payload = JSON.parse(JSON.stringify(input)); // throws on circular refs early assertSchema(methodName, payload); // validate against the plugin's method contract
Try / catch
try {
return await pluginRepo.callMethod({ pluginKey, methodName }, input, context);
} catch (e) {
if (String(e.message).startsWith('Plugin method call failed')) {
logger.error(`${(e as any).cause?.message ?? e}`); // real error from the plugin
throw new PluginInvocationError(pluginKey, methodName, e);
}
throw e;
} Prevention
- Version and validate the plugin method input contract (schema validation on both sides)
- Log the cause — the wrapper message only names label#method
- Keep plain JSON-serializable payloads (no circular refs, Dates, or class instances)
- Watch worker metrics (crashes, OOM) and pin plugin versions during rollouts
When it happens
Trigger: pool.acquire() or plugin.call(methodName, JSON.stringify(input), context) rejects — the plugin method threw, input was not JSON-serializable, the worker crashed mid-call, or the call timed out.
Common situations: Plugin method received unexpected input shape and threw internally; NaN/circular objects failing JSON.stringify; worker OOM or crash under load; bug in a newly deployed plugin version.
Understand the failure class
Background: "API request failed": what wrapped HTTP errors from external APIs mean and how to find the real cause — this error's family across 29 libraries.
Related errors
- Unable to instantiate plugin
- authToken is required
- Hostname did not match any listed in methods[].allowedHosts…
- Invalid token
- Invalid token: missing userId
AI-assisted analysis of immich-app/immich@e55ac299a4 (2026-09-15).
Data as JSON: /api/errors/52a0ec16d14f2287.
Report an issue: GitHub.
Appendix: source
Thrown at server/src/repositories/plugin.repository.ts:261
async callMethod<T>({ pluginKey, methodName }: PluginMethod, input: unknown, context?: unknown) {
const item = this.pluginMap.get(pluginKey);
if (!item) {
throw new Error(`No loaded plugin found for ${pluginKey}`);
}
const { pool, label } = item;
try {
const plugin = await pool.acquire();
try {
const result = await plugin.call(methodName, JSON.stringify(input), context);
return (result ? result.json() : result) as T;
} finally {
await pool.release(plugin);
}
} catch (error: Error | any) {
throw new Error(`Plugin method call failed: ${label}#${methodName}`, { cause: error });
}
}
}
View on GitHub (pinned to e55ac299a4)