immich-app/immich · warning · BadRequestException
Plugin not found
Error message
Plugin not found
What it means
Thrown by PluginService.get when pluginRepository.get(id) returns null. Despite being a lookup-by-id miss, it uses BadRequestException (HTTP 400) rather than NotFoundException. Plugins are workflow extensions registered in the plugin repository.
Source
Thrown at server/src/services/plugin.service.ts:25
PluginMethodSearchDto,
PluginResponseDto,
PluginSearchDto,
PluginTemplateResponseDto,
} from 'src/dtos/plugin.dto';
import { BaseService } from 'src/services/base.service';
import { isMethodCompatible } from 'src/utils/workflow';
@Injectable()
export class PluginService extends BaseService {
async search(dto: PluginSearchDto): Promise<PluginResponseDto[]> {
const plugins = await this.pluginRepository.search(dto);
return plugins.map((plugin) => mapPlugin(plugin));
}
async get(id: string): Promise<PluginResponseDto> {
const plugin = await this.pluginRepository.get(id);
if (!plugin) {
throw new BadRequestException('Plugin not found');
}
return mapPlugin(plugin);
}
async searchMethods(dto: PluginMethodSearchDto): Promise<PluginMethodResponseDto[]> {
const methods = await this.pluginRepository.searchMethods(dto);
return methods
.filter((method) => !dto.trigger || isMethodCompatible(method, dto.trigger))
.map((method) => mapMethod(method));
}
async searchTemplates(): Promise<PluginTemplateResponseDto[]> {
const plugins = await this.pluginRepository.search();
return plugins.flatMap((plugin) => plugin.templates.map((template) => mapTemplate(plugin, template)));
}
}
View on GitHub (pinned to 199723261c)
Solutions
- List plugins via GET /plugins (PluginService.search) to discover valid ids.
- Update the workflow definition to reference a current plugin id.
- Wrap plugin lookups in a try-catch and re-resolve via search when this fires.
Example fix
// before
const plugin = await this.pluginRepository.get(id);
if (!plugin) {
throw new BadRequestException('Plugin not found');
}
// after (use 404 for a missing resource)
const plugin = await this.pluginRepository.get(id);
if (!plugin) {
throw new NotFoundException(`Plugin ${id} not found`);
} Defensive patterns
Strategy: validation
Validate before calling
// Resolve plugin ids from the live list before referencing them.
const plugins = await pluginService.search({});
const valid = new Set(plugins.map((p) => p.id));
if (!valid.has(id)) {
throw new Error(`Plugin ${id} is not installed`);
}
await pluginService.get(id); Type guard
const isPlugin = (p: PluginResponseDto | null | undefined): p is PluginResponseDto => !!p && typeof p.id === 'string';
Try / catch
try {
return await pluginService.get(id);
} catch (e) {
if (e instanceof BadRequestException && /Plugin not found/i.test(e.message)) {
// re-resolve via search and surface a helpful message
const available = await pluginService.search({});
throw new Error(`Plugin ${id} not found. Installed: ${available.map((p) => p.id).join(', ')}`);
}
throw e;
} Prevention
- Re-resolve plugin ids from GET /plugins whenever the plugin set changes.
- Update workflow definitions after uninstalling or upgrading a plugin.
- Treat 'Plugin not found' as a configuration drift signal.
When it happens
Trigger: GET /plugins/{id} with an id that does not exist in the plugin repository; referencing a plugin id from a stale workflow definition.
Common situations: Workflow YAML references a plugin that was uninstalled; id copied with a trailing character; plugin migrated and the old id no longer resolves.
Related errors
- Invalid token: missing userId
- Invalid token
- Unknown method ${step.method}
- Method "${step.method}" is incompatible with workflow trigge
- API Key not found
AI-assisted analysis of immich-app/immich@199723261c (2026-08-12).
Data as JSON: /api/errors/d76c0f219d1ef31b.
Report an issue: GitHub.