{"record":{"id":"3901d8f2d071c4a0","repo":"router-for-me/CLIProxyAPI","slug":"target-executor-plugin-id-is-required","errorCode":null,"errorMessage":"target executor plugin id is required","messagePattern":"target executor plugin id is required","errorType":"http","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/pluginhost/executor_route.go","lineNumber":118,"sourceCode":"\treturn adapter.ExecuteStream(ctx, (*coreauth.Auth)(nil), req, opts)\n}\n\n// CountPluginExecutor executes a count-tokens request with the named plugin executor without changing the requested model.\nfunc (h *Host) CountPluginExecutor(ctx context.Context, pluginID string, req coreexecutor.Request, opts coreexecutor.Options) (coreexecutor.Response, error) {\n\tadapter, errAdapter := h.executorAdapterForPlugin(pluginID)\n\tif errAdapter != nil {\n\t\treturn coreexecutor.Response{}, errAdapter\n\t}\n\treturn adapter.CountTokens(ctx, (*coreauth.Auth)(nil), req, opts)\n}\n\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","sourceCodeStart":100,"sourceCodeEnd":136,"githubUrl":"https://github.com/router-for-me/CLIProxyAPI/blob/78f0c4079e3e6273d65d03b5549cffc898703264/internal/pluginhost/executor_route.go#L100-L136","documentation":"executorAdapterForPlugin trims the plugin ID and requires it to be non-empty. An empty or whitespace-only pluginID (for example a model name like \"::model\" or \"/some-model\" that yields an empty plugin segment) produces this error before any plugin lookup happens.","triggerScenarios":"Routing a request with a model identifier whose plugin prefix is empty after trimming: misconfigured model-to-plugin mapping, model string starting with the separator, or caller passing an unset pluginID variable.","commonSituations":"Config maps a model to a plugin executor but the plugin id field is left blank; dynamic code builds the pluginID from user input that is empty; a model alias regex produces an empty capture.","solutions":["Check the request's model/plugin routing configuration: the target plugin id must be a non-empty, trimmed identifier.","Validate the pluginID at the point where it is extracted (model name parsing) and reject malformed model strings early.","Confirm the plugin's registered id in its manifest matches what the router passes."],"exampleFix":"# before (config.yaml)\nmodels:\n  - name: \"my-model\"\n    plugin: \"\"   # empty -> error\n\n# after\nmodels:\n  - name: \"my-model\"\n    plugin: \"my-executor-plugin\"","handlingStrategy":"validation","validationCode":"pluginID = strings.TrimSpace(pluginID)\nif pluginID == \"\" {\n    return fmt.Errorf(\"model %q has no plugin prefix\", modelName)\n}","typeGuard":"func validPluginID(id string) bool {\n    id = strings.TrimSpace(id)\n    return id != \"\" && !strings.ContainsAny(id, \" \\t/\")\n}","tryCatchPattern":"adapter, err := host.ExecutorAdapterForPlugin(pluginID)\nif err != nil && strings.Contains(err.Error(), \"plugin id is required\") {\n    return fmt.Errorf(\"routing bug: empty plugin id extracted from model %q\", rawModel)\n}","preventionTips":["Validate model-name-to-plugin parsing with unit tests covering empty segments.","Reject malformed model identifiers at the API boundary, not in the executor layer."],"tags":["plugin","executor","routing","validation","pluginhost"],"backgroundTag":null,"analyzedSha":"78f0c4079e3e6273d65d03b5549cffc898703264","analyzedAt":"2026-08-15T12:26:37.444Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}