{"record":{"id":"6ba47cf85849ea08","repo":"router-for-me/CLIProxyAPI","slug":"invalid-request","errorCode":"invalid_request","errorMessage":"read install request: %w","messagePattern":"read install request: %w","errorType":"http","errorClass":null,"httpStatus":400,"severity":"error","filePath":"internal/api/handlers/management/plugin_store.go","lineNumber":433,"sourceCode":"\tcase pluginstore.InstallTypeGitHubRelease:\n\t\treleaseTag := strings.TrimSpace(result.ReleaseTag)\n\t\tif releaseTag == \"\" {\n\t\t\treturn pluginstore.Manifest{}, fmt.Errorf(\"release tag is required\")\n\t\t}\n\t\treturn pluginstore.ManifestFromRelease(source, plugin, pluginstore.Release{TagName: releaseTag})\n\tdefault:\n\t\treturn pluginstore.Manifest{}, fmt.Errorf(\"unsupported install type %q\", result.InstallType)\n\t}\n}\n\nfunc pluginInstallRequestedVersion(c *gin.Context) (string, error) {\n\trequestedVersion := strings.TrimSpace(c.Query(\"version\"))\n\tif c == nil || c.Request == nil || c.Request.Body == nil || c.Request.Body == http.NoBody {\n\t\treturn requestedVersion, nil\n\t}\n\tbody, errRead := io.ReadAll(c.Request.Body)\n\tif errRead != nil {\n\t\treturn \"\", fmt.Errorf(\"read install request: %w\", errRead)\n\t}\n\tif strings.TrimSpace(string(body)) == \"\" {\n\t\treturn requestedVersion, nil\n\t}\n\tvar req pluginInstallRequest\n\tif errDecode := json.Unmarshal(body, &req); errDecode != nil {\n\t\treturn \"\", fmt.Errorf(\"decode install request: %w\", errDecode)\n\t}\n\tbodyVersion := strings.TrimSpace(req.Version)\n\tif requestedVersion == \"\" {\n\t\treturn bodyVersion, nil\n\t}\n\tif bodyVersion == \"\" || normalizePluginStoreRequestedVersion(bodyVersion) == normalizePluginStoreRequestedVersion(requestedVersion) {\n\t\treturn requestedVersion, nil\n\t}\n\treturn \"\", fmt.Errorf(\"version query %q does not match request body version %q\", requestedVersion, bodyVersion)\n}\n","sourceCodeStart":415,"sourceCodeEnd":451,"githubUrl":"https://github.com/router-for-me/CLIProxyAPI/blob/78f0c4079e3e6273d65d03b5549cffc898703264/internal/api/handlers/management/plugin_store.go#L415-L451","documentation":"Returned by pluginInstallRequestedVersion (plugin_store.go:433) with code invalid_request when reading the install request body with io.ReadAll fails. The install endpoint accepts the version either as a query parameter or a JSON body; if the body stream errors (client disconnect, malformed chunked transfer encoding, connection reset), the read fails and the request is aborted before parsing.","triggerScenarios":"POST /management/plugin-store/install with a chunked/streaming body that is cut off mid-transfer; client dropping the connection while the server reads; proxy in front of the API mangling transfer encoding.","commonSituations":"Scripts piping request bodies that terminate early; flaky networks between an operator's machine and the management API; HTTP proxies re-encoding the body incorrectly.","solutions":["Retry the request — transient connection drops are the most common cause","Send the version as a query parameter and an empty body: ?version=1.2.3 (body reading is skipped when the body is empty or http.NoBody)","If behind a proxy, ensure Content-Length or proper chunked encoding is preserved","Check the client for premature stream closure (e.g. timeout killing the upload)"],"exampleFix":"# before: body upload interrupted\n# (curl killed mid-request -> \"read install request: ...\")\n\n# after: pass version via query string, no body needed\ncurl -X POST -H \"Authorization: Bearer $KEY\" \\\n  'http://127.0.0.1:8000/management/plugin-store/install?plugin_id=foo&version=1.2.3'","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"resp, err := httpClient.Do(req)\nif err != nil {\n\t// transient transport failure: rebuild and retry once\n\ttime.Sleep(time.Second)\n\tresp, err = httpClient.Do(cloneRequest(req))\n}\nif err != nil {\n\treturn err\n}","preventionTips":["Send the version in the query string with an empty body to sidestep body-read failures entirely","Ensure proxies preserve Content-Length or correct chunked encoding","Set sane client timeouts so uploads are not killed mid-body"],"tags":["plugins","plugin-store","http","request-body","transient"],"backgroundTag":null,"analyzedSha":"78f0c4079e3e6273d65d03b5549cffc898703264","analyzedAt":"2026-08-15T12:26:37.444Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}