router-for-me/CLIProxyAPI · error

plugin host is unavailable

Error message

plugin host is unavailable

What it means

executorAdapterForPlugin returns this when the receiver Host itself is nil. It is a programming/lifecycle error: caller invoked CountPluginExecutor/ExecutePluginExecutor-style routing on a nil *Host, typically because the plugin host was never initialized in this process or was already torn down and the pointer zeroed.

Source

Thrown at internal/pluginhost/executor_route.go:114

	adapter, errAdapter := h.executorAdapterForPlugin(pluginID)
	if errAdapter != nil {
		return nil, errAdapter
	}
	return adapter.ExecuteStream(ctx, (*coreauth.Auth)(nil), req, opts)
}

// CountPluginExecutor executes a count-tokens request with the named plugin executor without changing the requested model.
func (h *Host) CountPluginExecutor(ctx context.Context, pluginID string, req coreexecutor.Request, opts coreexecutor.Options) (coreexecutor.Response, error) {
	adapter, errAdapter := h.executorAdapterForPlugin(pluginID)
	if errAdapter != nil {
		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 {

View on GitHub (pinned to 78f0c4079e)

Solutions

  1. Ensure the plugin host is created and attached to the service before any request can be routed to a plugin executor.
  2. Guard call sites: skip plugin-executor routing when the host is nil rather than calling into it.
  3. If using official cmd/server, report a bug — this state should not be reachable there.

Example fix

// before
adapter, err := host.executorAdapterForPlugin(pluginID) // host may be nil

// after
if host == nil {
	return nil, fmt.Errorf("plugin routing requested but plugin host is not initialized")
}
adapter, err := host.executorAdapterForPlugin(pluginID)
Defensive patterns

Strategy: type-guard

Validate before calling

if host == nil {
    return fmt.Errorf("plugin executor routing skipped: plugin host not initialized")
}

Type guard

func pluginHostReady(h *pluginhost.Host) bool {
    return h != nil
}

Try / catch

// Go: defend with a nil-receiver check rather than catch
if h == nil {
    return coreexecutor.Response{}, fmt.Errorf("plugin host unavailable")
}
resp, err := h.CountPluginExecutor(ctx, pluginID, req, opts)

Prevention

When it happens

Trigger: Calling Host.CountPluginExecutor (or the execute route that uses executorAdapterForPlugin) on a nil host pointer: embedding code that skips plugin host setup but still routes model requests to plugin executors, or a code path that reads the host from an optional field that was never set.

Common situations: Custom embeddings that treat the plugin host as optional; races during shutdown where the host reference is cleared while a request is being routed; tests constructing partial service objects.

Related errors


AI-assisted analysis of router-for-me/CLIProxyAPI@78f0c4079e (2026-08-15). Data as JSON: /api/errors/3372674acdae1f96. Report an issue: GitHub.