router-for-me/CLIProxyAPI · error
plugin executor %s http request panic: %v
Error message
plugin executor %s http request panic: %v
What it means
The plugin's HttpRequest implementation panicked; the adapter's deferred recover() converted the panic into an error, nils the response, and fuses the plugin so later calls fail fast with 'unavailable'.
Source
Thrown at internal/pluginhost/adapters_executors.go:799
return coreexecutor.Response{
Payload: a.translateExecutorResponse(ctx, prepared, pluginResp.Payload, false, nil),
Metadata: cloneAnyMap(pluginResp.Metadata),
Headers: cloneHeader(pluginResp.Headers),
}, nil
}
func (a *executorAdapter) HttpRequest(ctx context.Context, auth *coreauth.Auth, req *http.Request) (resp *http.Response, err error) {
if a == nil || a.executor == nil || a.host.isPluginFused(a.pluginID) || !a.host.pluginIdentityCurrent(a.pluginID, a.path, a.version) {
return nil, fmt.Errorf("plugin executor %s is unavailable", a.Identifier())
}
if req == nil {
return nil, fmt.Errorf("plugin executor %s received nil HTTP request", a.Identifier())
}
defer func() {
if recovered := recover(); recovered != nil {
a.host.fusePlugin(a.pluginID, "Executor.HttpRequest", recovered)
resp = nil
err = fmt.Errorf("plugin executor %s http request panic: %v", a.Identifier(), recovered)
}
}()
body, errReadAll := readAndRestoreRequestBody(req)
if errReadAll != nil {
return nil, fmt.Errorf("read plugin http request body: %w", errReadAll)
}
pluginResp, errHTTPRequest := a.executor.HttpRequest(ctx, pluginapi.ExecutorHTTPRequest{
AuthID: authID(auth),
AuthProvider: authProvider(auth),
Method: req.Method,
URL: req.URL.String(),
Headers: cloneHeader(req.Header),
Body: bytes.Clone(body),
StorageJSON: storageJSONFromAuth(auth),
Metadata: cloneAnyMap(authMetadata(auth)),
Attributes: authAttributes(auth),
HTTPClient: a.host.newHTTPClient(auth, a.provider),
})View on GitHub (pinned to 78f0c4079e)
Solutions
- Check the recovered panic value in host logs and the Method/URL being proxied
- Fix the plugin's HttpRequest to validate AuthID/Headers/StorageJSON before dereferencing and to return errors instead of panicking
- Reinstall/reload the fixed plugin to clear the fuse
- Add plugin-side tests for empty-auth and unsupported-method requests
Defensive patterns
Strategy: try-catch
Try / catch
resp, err := adapter.HttpRequest(ctx, auth, req)
if err != nil {
if strings.Contains(err.Error(), "http request panic") {
return nil, status.Errorf(http.StatusBadGateway, "plugin proxy failed")
}
return nil, err
} Prevention
- In plugins, validate ExecutorHTTPRequest fields (Headers, StorageJSON, Method) before use
- Test plugin HTTP paths with anonymous auth and odd methods
- Alert on 'http request panic' — the plugin is fused after the first occurrence
When it happens
Trigger: Calling HttpRequest where the plugin panics — e.g. it dereferences fields of the ExecutorHTTPRequest struct it expects to be set (Headers map, StorageJSON) or mishandles a request method/URL it does not support.
Common situations: Plugin assumes auth data exists (StorageJSON empty for anonymous requests); plugin built against an older plugin API struct; unusual HTTP method or header shape reaching the plugin.
Related errors
- plugin executor %s stream panic: %v
- plugin executor %s count tokens panic: %v
- plugin executor %s refresh panic: %v
- auth provider panic: %v
- plugin executor %s is unavailable
AI-assisted analysis of router-for-me/CLIProxyAPI@78f0c4079e (2026-08-15).
Data as JSON: /api/errors/8d9ec208b664e9b0.
Report an issue: GitHub.