{"record":{"id":"99d2e4ba80a1538e","repo":"router-for-me/CLIProxyAPI","slug":"decode-plugin-envelope-s-w","errorCode":null,"errorMessage":"decode plugin envelope %s: %w","messagePattern":"decode plugin envelope (.+?): %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/pluginhost/rpc_client.go","lineNumber":161,"sourceCode":"\tif resp.Capabilities.ManagementAPI {\n\t\tplugin.Capabilities.ManagementAPI = adapter\n\t}\n\treturn plugin, nil\n}\n\nfunc callPlugin[T any](ctx context.Context, client pluginClient, method string, request any) (T, error) {\n\tvar zero T\n\trawRequest, errMarshal := json.Marshal(sanitizePluginRequest(request))\n\tif errMarshal != nil {\n\t\treturn zero, fmt.Errorf(\"marshal plugin request %s: %w\", method, errMarshal)\n\t}\n\trawResp, errCall := client.Call(ctx, method, rawRequest)\n\tif errCall != nil {\n\t\treturn zero, errCall\n\t}\n\tvar envelope pluginabi.Envelope\n\tif errUnmarshal := json.Unmarshal(rawResp, &envelope); errUnmarshal != nil {\n\t\treturn zero, fmt.Errorf(\"decode plugin envelope %s: %w\", method, errUnmarshal)\n\t}\n\tout, errDecode := decodeEnvelopeResult[T](envelope)\n\tif errDecode != nil {\n\t\tif !envelope.OK {\n\t\t\treturn zero, errDecode\n\t\t}\n\t\treturn zero, fmt.Errorf(\"decode plugin result %s: %w\", method, errDecode)\n\t}\n\treturn out, nil\n}\n\nfunc sanitizePluginRequest(request any) any {\n\tswitch req := request.(type) {\n\tcase pluginapi.AuthLoginStartRequest:\n\t\treq.HTTPClient = nil\n\t\treturn req\n\tcase pluginapi.AuthLoginPollRequest:\n\t\treq.HTTPClient = nil","sourceCodeStart":143,"sourceCodeEnd":179,"githubUrl":"https://github.com/router-for-me/CLIProxyAPI/blob/78f0c4079e3e6273d65d03b5549cffc898703264/internal/pluginhost/rpc_client.go#L143-L179","documentation":"callPlugin in internal/pluginhost/rpc_client.go received a response from the out-of-process plugin but json.Unmarshal of the raw bytes into pluginabi.Envelope failed. Every plugin RPC reply must be a JSON envelope ({ok, result, error}); anything else — HTML error pages, plain-text output, a crashed/exiting plugin's partial write, or a protocol-version mismatch — lands here.","triggerScenarios":"client.Call returns bytes that are not valid JSON or not an envelope object: plugin wrote to stdout instead of the RPC channel, plugin crashed mid-response, plugin built against an older/newer pluginabi with a different framing, or a wrapper script printed extra output.","commonSituations":"Plugin executable prints debug logs to stdout; a shell wrapper (npx, pip run) prepends warnings; plugin and host built from different CLIProxyAPI versions; plugin panics after writing a partial line.","solutions":["Run the plugin binary standalone and ensure its stdout carries only the JSON envelope (route logs to stderr)","Rebuild/reinstall the plugin against the same version as the host so pluginabi framing matches","Check the plugin's own logs/exit status for a crash during the call","If a wrapper script is used, silence its banners or have it exec the plugin directly"],"exampleFix":"// plugin: before\nfmt.Println(\"starting up\") // pollutes stdout envelope stream\n\n// plugin: after\nfmt.Fprintln(os.Stderr, \"starting up\") // logs go to stderr only","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"resp, err := callPlugin[Result](ctx, client, \"Method\", req)\nif err != nil && strings.Contains(err.Error(), \"decode plugin envelope\") {\n    client.Restart() // plugin stdout is polluted or process is wedged\n    resp, err = callPlugin[Result](ctx, client, \"Method\", req)\n    if err != nil {\n        return fmt.Errorf(\"plugin protocol broken after restart: %w\", err)\n    }\n}","preventionTips":["Keep plugin stdout reserved for the RPC envelope; send all plugin logs to stderr","Build plugins and host from the same release so the envelope schema matches","Avoid wrapper scripts that print banners before exec-ing the plugin"],"tags":["rpc","plugin","json","protocol"],"backgroundTag":null,"analyzedSha":"78f0c4079e3e6273d65d03b5549cffc898703264","analyzedAt":"2026-08-15T12:26:37.444Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}