{"record":{"id":"2e817618aa360b02","repo":"m1k1o/neko","slug":"plugin-s-not-found","errorCode":null,"errorMessage":"plugin '%s' not found","messagePattern":"plugin '(.+?)' not found","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"server/internal/plugins/manager.go","lineNumber":151,"sourceCode":"\t\t} else {\n\t\t\tmanager.logger.Err(err).Msg(\"failed to start plugins, skipping...\")\n\t\t}\n\t}\n}\n\nfunc (manager *ManagerCtx) Shutdown() error {\n\t_ = manager.plugins.forEach(func(d *dependency) error {\n\t\terr := d.plugin.Shutdown()\n\t\tmanager.logger.Err(err).Str(\"plugin\", d.plugin.Name()).Msg(\"plugin shutdown\")\n\t\treturn nil\n\t})\n\treturn nil\n}\n\nfunc (manager *ManagerCtx) LookupService(pluginName string) (any, error) {\n\tplug, ok := manager.plugins.findPlugin(pluginName)\n\tif !ok {\n\t\treturn nil, fmt.Errorf(\"plugin '%s' not found\", pluginName)\n\t}\n\n\texpPlug, ok := plug.plugin.(types.ExposablePlugin)\n\tif !ok {\n\t\treturn nil, fmt.Errorf(\"plugin '%s' is not exposable\", pluginName)\n\t}\n\n\treturn expPlug.ExposeService(), nil\n}\n\nfunc (manager *ManagerCtx) Metadata() []types.PluginMetadata {\n\tvar plugins []types.PluginMetadata\n\n\t_ = manager.plugins.forEach(func(d *dependency) error {\n\t\tdependsOn := make([]string, 0)\n\t\tdeps, isDependalbe := d.plugin.(types.DependablePlugin)\n\t\tif isDependalbe {\n\t\t\tdependsOn = deps.DependsOn()","sourceCodeStart":133,"sourceCodeEnd":169,"githubUrl":"https://github.com/m1k1o/neko/blob/b0f01cedea68893e85a3fd852c0521238c285695/server/internal/plugins/manager.go#L133-L169","documentation":"Returned by Manager.LookupService when no registered plugin matches pluginName (findPlugin on the dependency registry fails). LookupService exposes an exposable plugin's service; the lookup is purely by the name returned by plugin.Name().","triggerScenarios":"Calling LookupService(name) where name was never registered — typo in the name, the plugin failed to load/start earlier, or the plugin was removed (e.g. its deps entry deleted by the cycle-break in addPlugin).","commonSituations":"Typo'd plugin name in handler wiring; a plugin whose load failed at startup (see errors 67/68) so it never entered the registry; requesting a service before plugin load/init completed.","solutions":["Verify the exact plugin name (the value of plugin.Name()) and fix the LookupService argument","Check startup logs for plugin load errors — if the plugin failed to load it will not be in the registry","Ensure plugins are loaded and InitConfigs/Start completed before LookupService is called","Guard the call site: treat the error as expected for optional plugins and fall back gracefully"],"exampleFix":"// before\nsvc, err := manager.LookupService(\"file_tranfer\") // typo\n\n// after\nsvc, err := manager.LookupService(\"filetransfer\")\nif err != nil {\n    log.Warn().Err(err).Str(\"plugin\", name).Msg(\"plugin service unavailable\")\n    return\n}","handlingStrategy":"fallback","validationCode":"// ensure plugin load phase completed and name exists before lookup\nif len(manager.Plugins()) == 0 {\n    return fmt.Errorf(\"plugins not loaded yet\")\n}\nif _, ok := manager.plugins.findPlugin(name); !ok {\n    return fmt.Errorf(\"unknown plugin %q; known: %v\", name, knownNames())\n}","typeGuard":null,"tryCatchPattern":"svc, err := manager.LookupService(name)\nif err != nil {\n    if strings.Contains(err.Error(), \"not found\") {\n        // optional plugin: degrade gracefully\n        return nil, nil\n    }\n    return nil, err\n}","preventionTips":["Copy plugin names from plugin.Name() / a shared constant, never by hand","Gate service lookups behind plugin startup readiness","Log the list of registered plugin names at startup to make typos obvious"],"tags":["go","plugins","lookup","not-found"],"backgroundTag":"plugin-not-found","analyzedSha":"b0f01cedea68893e85a3fd852c0521238c285695","analyzedAt":"2026-09-01T10:35:56.638Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T15:18:49.778Z"}