router-for-me/CLIProxyAPI · error
plugin executor %s is unavailable
Error message
plugin executor %s is unavailable
What it means
The requested plugin record exists and is active, but isPluginFused reports it as fused — the host disabled it after repeated panics in its capability calls. All executor routing to it is refused with this error naming the plugin ID.
Source
Thrown at internal/pluginhost/executor_route.go:125
return coreexecutor.Response{}, errAdapter
}
return adapter.CountTokens(ctx, (*coreauth.Auth)(nil), req, opts)
}
func (h *Host) executorAdapterForPlugin(pluginID string) (*executorAdapter, error) {
if h == nil {
return nil, fmt.Errorf("plugin host is unavailable")
}
pluginID = strings.TrimSpace(pluginID)
if pluginID == "" {
return nil, fmt.Errorf("target executor plugin id is required")
}
for _, record := range h.activeRecords() {
if record.id != pluginID {
continue
}
if h.isPluginFused(record.id) {
return nil, fmt.Errorf("plugin executor %s is unavailable", pluginID)
}
executor := record.plugin.Capabilities.Executor
if executor == nil {
return nil, fmt.Errorf("plugin %s does not declare an executor", pluginID)
}
provider, okProvider := h.executorProvider(record, executor)
if !okProvider {
return nil, fmt.Errorf("plugin executor %s has no provider identifier", pluginID)
}
registration := newExecutorAdapterRegistration(h, record, provider, executor)
return registration.adapter, nil
}
return nil, fmt.Errorf("plugin executor %s not found", pluginID)
}
View on GitHub (pinned to 78f0c4079e)
Solutions
- Search logs for fusePlugin entries mentioning this plugin ID to find the originating panic and capability.
- Fix or update the plugin (see the panic error's guidance), then restart/reload the host to clear the fuse.
- If panics are load-triggered, capture the plugin's stack trace by running it standalone under the same inputs.
- Consider removing the plugin from config until fixed so routing fails fast at config time rather than per-request.
Defensive patterns
Strategy: fallback
Validate before calling
if host.IsPluginFused(pluginID) {
return fmt.Errorf("plugin %s is disabled after repeated panics; see fuse logs", pluginID)
} Type guard
func executorUsable(h *pluginhost.Host, pluginID string) bool {
for _, rec := range h.ActiveRecords() {
if rec.ID == pluginID {
return !h.IsPluginFused(pluginID) && rec.Plugin.Capabilities.Executor != nil
}
}
return false
} Try / catch
resp, err := host.ExecutePluginExecutor(ctx, pluginID, req, opts)
if err != nil && strings.Contains(err.Error(), "is unavailable") {
// fused plugin: permanent until restart; route to fallback provider/executor
return routeToFallback(model, req, opts)
} Prevention
- Configure fallback executors/models so fused plugins degrade gracefully.
- Monitor fuse events and auto-restart or alert when a plugin trips them.
- Test plugins under load before deploying — fuses usually come from load-triggered panics.
When it happens
Trigger: Requesting execution/count-tokens on a plugin whose executor (or other capability) panicked enough times to trip the fuse; the fuse log entries (from fusePlugin) name the capability and recovered value that caused it.
Common situations: A buggy plugin build that panics under load; plugin incompatible with the current host API causing repeated panics; leftover fuse state after an earlier incident in a long-running process.
Related errors
- auth provider panic: %v
- plugin executor %s stream panic: %v
- plugin executor %s refresh panic: %v
- plugin executor %s count tokens panic: %v
- plugin executor %s http request panic: %v
AI-assisted analysis of router-for-me/CLIProxyAPI@78f0c4079e (2026-08-15).
Data as JSON: /api/errors/b04c06adf2fef165.
Report an issue: GitHub.