router-for-me/CLIProxyAPI · error

ExecuteModel requires Stream=false

Error message

ExecuteModel requires Stream=false

What it means

Programmer error in the plugin/host model API: ExecuteModel() only performs non-streaming internal model requests, so calling it with ModelExecutionRequest{Stream: true} is rejected immediately with a mode error before any dispatch. The streaming counterpart is ExecuteModelStream().

Source

Thrown at sdk/api/handlers/model_execution.go:99

// Error returns the stream error message or the HTTP status text.
func (e *ModelExecutionStreamError) Error() string {
	if e == nil {
		return ""
	}
	if e.Message != "" {
		return e.Message
	}
	return http.StatusText(e.StatusCode)
}

// ExecuteModel executes an internal non-streaming model request.
// Host model callbacks are non-recursive for their caller: when
// skip plugin IDs are set, that plugin's interceptors and router are skipped
// for the nested model execution while other plugins may still run.
func (h *BaseAPIHandler) ExecuteModel(ctx context.Context, req ModelExecutionRequest) (ModelExecutionResponse, *interfaces.ErrorMessage) {
	if req.Stream {
		return ModelExecutionResponse{}, modelExecutionModeError("ExecuteModel requires Stream=false")
	}
	body, headers, errMsg := h.executeWithAuthManagerFormats(ctx, req.EntryProtocol, req.ExitProtocol, req.Model, cloneBytes(req.Body), req.Alt, false, modelExecutionOptions{
		Headers:                 req.Headers,
		Query:                   req.Query,
		InternalSource:          true,
		SkipInterceptorPluginID: req.SkipInterceptorPluginID,
		SkipRouterPluginID:      req.SkipRouterPluginID,
	})
	if errMsg != nil {
		return ModelExecutionResponse{}, errMsg
	}
	return ModelExecutionResponse{
		StatusCode: http.StatusOK,
		Headers:    cloneHeader(headers),
		Body:       cloneBytes(body),
	}, nil
}

View on GitHub (pinned to 78f0c4079e)

Solutions

  1. Set Stream: false in the request, or remove the field (zero value is false)
  2. If you actually need chunks, call ExecuteModelStream with Stream: true instead

Example fix

// before
resp, errMsg := h.ExecuteModel(ctx, ModelExecutionRequest{Model: name, Body: body, Stream: true})
// after
resp, errMsg := h.ExecuteModel(ctx, ModelExecutionRequest{Model: name, Body: body, Stream: false})
Defensive patterns

Strategy: validation

Validate before calling

if req.Stream {
    return nil, fmt.Errorf("use ExecuteModelStream for streaming; got Stream=true on ExecuteModel")
}
resp, errMsg := h.ExecuteModel(ctx, req)

Type guard

func isNonStreamingExecution(req ModelExecutionRequest) bool {
    return !req.Stream
}

Try / catch

// Error is returned in-band via *interfaces.ErrorMessage, not a Go error:
resp, errMsg := h.ExecuteModel(ctx, req)
if errMsg != nil && strings.Contains(errMsg.Error(), "requires Stream=false") {
    log.Error("programmer error: passed a streaming request to ExecuteModel")
}

Prevention

When it happens

Trigger: A plugin or internal caller constructs a ModelExecutionRequest with Stream: true and passes it to BaseAPIHandler.ExecuteModel.

Common situations: Porting code between the streaming and non-streaming variants; copy-paste of a request struct that was originally built for the stream API.

Related errors


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