{"record":{"id":"294660b15f6929ca","repo":"m1k1o/neko","slug":"plugin-s-is-not-exposable","errorCode":null,"errorMessage":"plugin '%s' is not exposable","messagePattern":"plugin '(.+?)' is not exposable","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"server/internal/plugins/manager.go","lineNumber":156,"sourceCode":"\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()\n\t\t}\n\n\t\t_, isExposable := d.plugin.(types.ExposablePlugin)\n\n\t\tplugins = append(plugins, types.PluginMetadata{","sourceCodeStart":138,"sourceCodeEnd":174,"githubUrl":"https://github.com/m1k1o/neko/blob/b0f01cedea68893e85a3fd852c0521238c285695/server/internal/plugins/manager.go#L138-L174","documentation":"LookupService returns a plugin's exposed service, but only plugins implementing the types.ExposablePlugin interface can be exposed. When the named plugin exists but its implementation does not satisfy that interface, the manager rejects the lookup with this error. It is a capability mismatch, not a missing-plugin condition.","triggerScenarios":"Calling Manager.LookupService(pluginName) for a plugin that is registered but whose main type does not implement types.ExposablePlugin (i.e., the type assertion plug.plugin.(types.ExposablePlugin) fails at server/internal/plugins/manager.go:156).","commonSituations":"Requesting a service from a plugin that only implements basic lifecycle (Load/Start/Stop) such as an audio/device plugin; misconfiguring the client to look up a plugin by the wrong name so a non-exposable plugin is matched; after refactoring a plugin and accidentally dropping the ExposeService method so it no longer satisfies the interface.","solutions":["Verify the plugin name passed to LookupService actually refers to a plugin that exposes a service (grep the plugin registry for which plugins implement ExposablePlugin).","Make the plugin implement types.ExposablePlugin by adding an ExposeService() method with the required signature.","If the plugin intentionally has no service, stop calling LookupService for it and interact via its normal plugin interface instead."],"exampleFix":"// before\nfunc (p *MyPlugin) Start() error { ... } // no ExposeService\n\n// after\nfunc (p *MyPlugin) ExposeService() interface{} {\n    return p.service // implement types.ExposablePlugin\n}","handlingStrategy":"type-guard","validationCode":"plug, err := registry.Get(pluginName)\nif err != nil { return err }\nif _, ok := plug.(types.ExposablePlugin); !ok {\n    return fmt.Errorf(\"plugin %q does not expose a service\", pluginName)\n}","typeGuard":"func asExposable(p types.Plugin) (types.ExposablePlugin, bool) {\n    ep, ok := p.(types.ExposablePlugin)\n    return ep, ok\n}","tryCatchPattern":"svc, err := manager.LookupService(pluginName)\nif err != nil {\n    if strings.Contains(err.Error(), \"is not exposable\") {\n        // fall back to non-service plugin interaction\n        return nil\n    }\n    return err\n}","preventionTips":["Maintain a compile-time assertion in each plugin: `var _ types.ExposablePlugin = (*MyPlugin)(nil)`.","Document which plugins expose services and only call LookupService for those.","After refactoring a plugin, re-run interface assertions/compilation before deploying."],"tags":["plugin","interface-not-implemented","go"],"backgroundTag":"interface-not-implemented","analyzedSha":"b0f01cedea68893e85a3fd852c0521238c285695","analyzedAt":"2026-09-01T10:35:56.638Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T15:18:49.778Z"}