router-for-me/CLIProxyAPI · error
invalid_request
invalid_request
Error message
read install request: %w
What it means
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.
Source
Thrown at internal/api/handlers/management/plugin_store.go:433
case pluginstore.InstallTypeGitHubRelease:
releaseTag := strings.TrimSpace(result.ReleaseTag)
if releaseTag == "" {
return pluginstore.Manifest{}, fmt.Errorf("release tag is required")
}
return pluginstore.ManifestFromRelease(source, plugin, pluginstore.Release{TagName: releaseTag})
default:
return pluginstore.Manifest{}, fmt.Errorf("unsupported install type %q", result.InstallType)
}
}
func pluginInstallRequestedVersion(c *gin.Context) (string, error) {
requestedVersion := strings.TrimSpace(c.Query("version"))
if c == nil || c.Request == nil || c.Request.Body == nil || c.Request.Body == http.NoBody {
return requestedVersion, nil
}
body, errRead := io.ReadAll(c.Request.Body)
if errRead != nil {
return "", fmt.Errorf("read install request: %w", errRead)
}
if strings.TrimSpace(string(body)) == "" {
return requestedVersion, nil
}
var req pluginInstallRequest
if errDecode := json.Unmarshal(body, &req); errDecode != nil {
return "", fmt.Errorf("decode install request: %w", errDecode)
}
bodyVersion := strings.TrimSpace(req.Version)
if requestedVersion == "" {
return bodyVersion, nil
}
if bodyVersion == "" || normalizePluginStoreRequestedVersion(bodyVersion) == normalizePluginStoreRequestedVersion(requestedVersion) {
return requestedVersion, nil
}
return "", fmt.Errorf("version query %q does not match request body version %q", requestedVersion, bodyVersion)
}
View on GitHub (pinned to 78f0c4079e)
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)
Example fix
# before: body upload interrupted # (curl killed mid-request -> "read install request: ...") # after: pass version via query string, no body needed curl -X POST -H "Authorization: Bearer $KEY" \ 'http://127.0.0.1:8000/management/plugin-store/install?plugin_id=foo&version=1.2.3'
Defensive patterns
Strategy: retry
Try / catch
resp, err := httpClient.Do(req)
if err != nil {
// transient transport failure: rebuild and retry once
time.Sleep(time.Second)
resp, err = httpClient.Do(cloneRequest(req))
}
if err != nil {
return err
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- failed to read body
- plugin_manifest_invalid
- plugin_install_failed
- plugin_manifest_failed
- read plugin http request body: %w
AI-assisted analysis of router-for-me/CLIProxyAPI@78f0c4079e (2026-08-15).
Data as JSON: /api/errors/6ba47cf85849ea08.
Report an issue: GitHub.