{"record":{"id":"3e2b689da2c82427","repo":"router-for-me/CLIProxyAPI","slug":"plugin-s-does-not-declare-an-executor","errorCode":null,"errorMessage":"plugin %s does not declare an executor","messagePattern":"plugin (.+?) does not declare an executor","errorType":"http","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/pluginhost/executor_route.go","lineNumber":129,"sourceCode":"\nfunc (h *Host) executorAdapterForPlugin(pluginID string) (*executorAdapter, error) {\n\tif h == nil {\n\t\treturn nil, fmt.Errorf(\"plugin host is unavailable\")\n\t}\n\tpluginID = strings.TrimSpace(pluginID)\n\tif pluginID == \"\" {\n\t\treturn nil, fmt.Errorf(\"target executor plugin id is required\")\n\t}\n\tfor _, record := range h.activeRecords() {\n\t\tif record.id != pluginID {\n\t\t\tcontinue\n\t\t}\n\t\tif h.isPluginFused(record.id) {\n\t\t\treturn nil, fmt.Errorf(\"plugin executor %s is unavailable\", pluginID)\n\t\t}\n\t\texecutor := record.plugin.Capabilities.Executor\n\t\tif executor == nil {\n\t\t\treturn nil, fmt.Errorf(\"plugin %s does not declare an executor\", pluginID)\n\t\t}\n\t\tprovider, okProvider := h.executorProvider(record, executor)\n\t\tif !okProvider {\n\t\t\treturn nil, fmt.Errorf(\"plugin executor %s has no provider identifier\", pluginID)\n\t\t}\n\t\tregistration := newExecutorAdapterRegistration(h, record, provider, executor)\n\t\treturn registration.adapter, nil\n\t}\n\treturn nil, fmt.Errorf(\"plugin executor %s not found\", pluginID)\n}\n","sourceCodeStart":111,"sourceCodeEnd":140,"githubUrl":"https://github.com/router-for-me/CLIProxyAPI/blob/78f0c4079e3e6273d65d03b5549cffc898703264/internal/pluginhost/executor_route.go#L111-L140","documentation":"The plugin with the given ID is active but its manifest/capability set has no Executor capability (record.plugin.Capabilities.Executor == nil). The host was asked to build an executor adapter for a plugin that simply does not provide model execution.","triggerScenarios":"Routing execution to a plugin that only provides auth, command-line, or router capabilities; typo in the plugin id in model routing config that happens to match a non-executor plugin; plugin version that dropped or renamed the executor capability.","commonSituations":"Config lists the wrong plugin id for a model; plugin downgrade where executor support was removed; a manifest parsing issue silently dropping the Executor capability.","solutions":["Verify the plugin's manifest declares an executor capability and that its id matches the routing config exactly.","Correct the model-to-plugin mapping in config to point at the plugin that actually provides the executor.","Update/reinstall the plugin to a version that declares Executor."],"exampleFix":"# before (config.yaml)\nmodels:\n  - name: \"my-model\"\n    plugin: \"my-auth-plugin\"     # no executor capability\n\n# after\nmodels:\n  - name: \"my-model\"\n    plugin: \"my-executor-plugin\" # declares Executor","handlingStrategy":"validation","validationCode":"for _, rec := range host.ActiveRecords() {\n    if rec.ID == wantedPluginID && rec.Plugin.Capabilities.Executor != nil {\n        return nil // routing target is valid\n    }\n}\nreturn fmt.Errorf(\"plugin %s has no executor capability\", wantedPluginID)","typeGuard":"func pluginDeclaresExecutor(rec pluginRecord) bool {\n    return rec.Plugin.Capabilities.Executor != nil\n}","tryCatchPattern":"adapter, err := host.ExecutorAdapterForPlugin(pluginID)\nif err != nil && strings.Contains(err.Error(), \"does not declare an executor\") {\n    return configErrorf(\"model %s routed to non-executor plugin %s; fix models config\", model, pluginID)\n}","preventionTips":["Validate model-routing config against installed plugin capabilities at startup.","Keep a catalog of which installed plugins provide executors versus auth-only."],"tags":["plugin","executor","capabilities","routing","pluginhost"],"backgroundTag":null,"analyzedSha":"78f0c4079e3e6273d65d03b5549cffc898703264","analyzedAt":"2026-08-15T12:26:37.444Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}