{"record":{"id":"df24671356e654ff","repo":"router-for-me/CLIProxyAPI","slug":"read-plugin-http-request-body-w","errorCode":null,"errorMessage":"read plugin http request body: %w","messagePattern":"read plugin http request body: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/pluginhost/adapters_executors.go","lineNumber":804,"sourceCode":"}\n\nfunc (a *executorAdapter) HttpRequest(ctx context.Context, auth *coreauth.Auth, req *http.Request) (resp *http.Response, err error) {\n\tif a == nil || a.executor == nil || a.host.isPluginFused(a.pluginID) || !a.host.pluginIdentityCurrent(a.pluginID, a.path, a.version) {\n\t\treturn nil, fmt.Errorf(\"plugin executor %s is unavailable\", a.Identifier())\n\t}\n\tif req == nil {\n\t\treturn nil, fmt.Errorf(\"plugin executor %s received nil HTTP request\", a.Identifier())\n\t}\n\tdefer func() {\n\t\tif recovered := recover(); recovered != nil {\n\t\t\ta.host.fusePlugin(a.pluginID, \"Executor.HttpRequest\", recovered)\n\t\t\tresp = nil\n\t\t\terr = fmt.Errorf(\"plugin executor %s http request panic: %v\", a.Identifier(), recovered)\n\t\t}\n\t}()\n\tbody, errReadAll := readAndRestoreRequestBody(req)\n\tif errReadAll != nil {\n\t\treturn nil, fmt.Errorf(\"read plugin http request body: %w\", errReadAll)\n\t}\n\tpluginResp, errHTTPRequest := a.executor.HttpRequest(ctx, pluginapi.ExecutorHTTPRequest{\n\t\tAuthID:       authID(auth),\n\t\tAuthProvider: authProvider(auth),\n\t\tMethod:       req.Method,\n\t\tURL:          req.URL.String(),\n\t\tHeaders:      cloneHeader(req.Header),\n\t\tBody:         bytes.Clone(body),\n\t\tStorageJSON:  storageJSONFromAuth(auth),\n\t\tMetadata:     cloneAnyMap(authMetadata(auth)),\n\t\tAttributes:   authAttributes(auth),\n\t\tHTTPClient:   a.host.newHTTPClient(auth, a.provider),\n\t})\n\tif errHTTPRequest != nil {\n\t\treturn nil, errHTTPRequest\n\t}\n\tstatus := pluginResp.StatusCode\n\tif status == 0 {","sourceCodeStart":786,"sourceCodeEnd":822,"githubUrl":"https://github.com/router-for-me/CLIProxyAPI/blob/78f0c4079e3e6273d65d03b5549cffc898703264/internal/pluginhost/adapters_executors.go#L786-L822","documentation":"The host failed while reading the inbound *http.Request body (via readAndRestoreRequestBody) before handing it to the plugin. The underlying io.ReadAll error is wrapped, and the original request body is restored so it can be retried or logged. The request never reached the plugin.","triggerScenarios":"The request's Body reader returns an error mid-read — e.g. client disconnected mid-upload, a Content-Length mismatch, a gzip body that fails decompression, or a body already consumed and closed by earlier middleware.","commonSituations":"Client drops the connection during a large upload; middleware chain reading/closing the body before the plugin proxy; decompressing reverse proxy in front of the host.","solutions":["Inspect the wrapped error (%w) — http: unexpected EOF means client disconnect, http: request body too large means body limits","Ensure middleware that reads the request body resets it (req.Body = io.NopCloser(bytes.NewBuffer(...))) before the plugin proxy runs","Raise server body-size limits if large uploads are legitimate","Retry idempotent requests after transient network failures"],"exampleFix":null,"handlingStrategy":"retry","validationCode":"// Before proxying, ensure the body is re-readable and intact:\nif req.Body != nil {\n    body, readErr := io.ReadAll(req.Body)\n    req.Body.Close()\n    if readErr != nil { return readErr }\n    req.Body = io.NopCloser(bytes.NewReader(body))\n}","typeGuard":null,"tryCatchPattern":"resp, err := adapter.HttpRequest(ctx, auth, req)\nif err != nil {\n    var netErr net.Error\n    if errors.Is(err, context.Canceled) || errors.As(err, &netErr) {\n        return nil, err // client gone or transient; safe to abort or retry once\n    }\n    return nil, err\n}","preventionTips":["Reset request bodies in middleware after reading (io.NopCloser over a buffer)","Configure realistic body-size limits","Check Content-Length vs actual bytes at the trust boundary"],"tags":["plugin","http","request-body","io","middleware"],"backgroundTag":null,"analyzedSha":"78f0c4079e3e6273d65d03b5549cffc898703264","analyzedAt":"2026-08-15T12:26:37.444Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}