router-for-me/CLIProxyAPI · error

host model executor is unavailable

Error message

host model executor is unavailable

What it means

During a host.model.execute callback from a plugin, the host asks its current model executor (h.currentModelExecutor()) to perform the upstream model call. If that returns nil — no executor is currently installed on the host — the callback fails with this error and the plugin's nested model call is rejected.

Source

Thrown at internal/pluginhost/host_callbacks.go:279

	var req rpcStreamCloseRequest
	if errUnmarshal := json.Unmarshal(request, &req); errUnmarshal != nil {
		return nil, fmt.Errorf("decode stream close request: %w", errUnmarshal)
	}
	h.streams.close(req.StreamID, req.Error)
	return marshalRPCResult(rpcEmptyResponse{})
}

func (h *Host) callHostModelExecute(ctx context.Context, request []byte) ([]byte, error) {
	var req rpcHostModelExecutionRequest
	if errUnmarshal := json.Unmarshal(request, &req); errUnmarshal != nil {
		return nil, fmt.Errorf("decode host model execution request: %w", errUnmarshal)
	}
	if req.Stream {
		return nil, fmt.Errorf("host.model.execute requires stream=false")
	}
	executor := h.currentModelExecutor()
	if executor == nil {
		return nil, fmt.Errorf("host model executor is unavailable")
	}
	skipPluginID := h.callbackCallerPluginID(ctx, req.HostCallbackID)
	ctx = h.resolveCallbackContext(req.HostCallbackID, ctx)
	resp, errMsg := executor.ExecuteModel(ctx, modelExecutionRequestFromPlugin(req.HostModelExecutionRequest, skipPluginID))
	if errMsg != nil {
		return nil, modelExecutionError(errMsg)
	}
	return marshalRPCResult(pluginapi.HostModelExecutionResponse{
		StatusCode: resp.StatusCode,
		Headers:    cloneHeader(resp.Headers),
		Body:       append([]byte(nil), resp.Body...),
	})
}

func modelExecutionRequestFromPlugin(req pluginapi.HostModelExecutionRequest, skipPluginID string) handlers.ModelExecutionRequest {
	return handlers.ModelExecutionRequest{
		EntryProtocol:           req.EntryProtocol,
		ExitProtocol:            req.ExitProtocol,

View on GitHub (pinned to 78f0c4079e)

Solutions

  1. Defer plugin work that needs host.model.execute until the host signals readiness (executor installed).
  2. In embeddings, wire the model executor into the host before loading plugins that use host model callbacks.
  3. During shutdown, stop plugins (or their callbacks) before clearing the current model executor.

Example fix

// plugin side, before
func (p *Plugin) OnLoad(ctx context.Context) error {
	_, err := p.Host.ModelExecute(ctx, req) // executor may not be ready yet
	return err
}

// after
func (p *Plugin) OnReady(ctx context.Context) error {
	_, err := p.Host.ModelExecute(ctx, req) // host signals readiness first
	return err
}
Defensive patterns

Strategy: type-guard

Validate before calling

// Plugin side: check readiness before host.model.execute
if !host.ModelExecutorReady() {
    return fmt.Errorf("host model executor not ready; defer this operation")
}

Type guard

func hostModelExecutionReady(h *pluginhost.Host) bool {
    return h != nil && h.CurrentModelExecutor() != nil
}

Try / catch

resp, err := host.ModelExecute(ctx, req)
if err != nil && strings.Contains(err.Error(), "host model executor is unavailable") {
    // retry after a readiness delay, or queue the work until host signals ready
    return scheduleAfterReady(ctx, req)
}

Prevention

When it happens

Trigger: A plugin invokes host.model.execute while the host has no current model executor: service still initializing, executor torn down during shutdown, or an embedded host configured without the model execution pipeline.

Common situations: Plugin performs a host-assisted model call during its own startup/registration before the executor is ready; embedding sdk/cliproxy without wiring the model executor; shutdown race where a plugin callback arrives after the executor was cleared.

Related errors


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