{"record":{"id":"3125524abfcf697b","repo":"router-for-me/CLIProxyAPI","slug":"s-312552","errorCode":null,"errorMessage":"%s","messagePattern":"%s","errorType":"http","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/pluginhost/rpc_client.go","lineNumber":313,"sourceCode":"\tvar envelope pluginabi.Envelope\n\tif errUnmarshal := json.Unmarshal(raw, &envelope); errUnmarshal != nil {\n\t\treturn false\n\t}\n\treturn !envelope.OK && envelope.Error != nil\n}\n\nfunc decodeEnvelopeResult[T any](envelope pluginabi.Envelope) (T, error) {\n\tvar zero T\n\tif !envelope.OK {\n\t\tif envelope.Error != nil {\n\t\t\tmessage := strings.TrimSpace(envelope.Error.Message)\n\t\t\tif message == \"\" {\n\t\t\t\tmessage = \"plugin call failed\"\n\t\t\t}\n\t\t\tif envelope.Error.HTTPStatus > 0 {\n\t\t\t\treturn zero, rpcPluginError{message: message, statusCode: envelope.Error.HTTPStatus}\n\t\t\t}\n\t\t\treturn zero, fmt.Errorf(\"%s\", message)\n\t\t}\n\t\treturn zero, fmt.Errorf(\"plugin call failed\")\n\t}\n\tif len(envelope.Result) == 0 {\n\t\treturn zero, nil\n\t}\n\tvar out T\n\tif errDecode := json.Unmarshal(envelope.Result, &out); errDecode != nil {\n\t\treturn zero, errDecode\n\t}\n\treturn out, nil\n}\n\nfunc marshalRPCEnvelope(result json.RawMessage) ([]byte, error) {\n\tif result == nil {\n\t\tresult = json.RawMessage(`{}`)\n\t}\n\treturn json.Marshal(pluginabi.Envelope{OK: true, Result: result})","sourceCodeStart":295,"sourceCodeEnd":331,"githubUrl":"https://github.com/router-for-me/CLIProxyAPI/blob/78f0c4079e3e6273d65d03b5549cffc898703264/internal/pluginhost/rpc_client.go#L295-L331","documentation":"A failing plugin RPC whose envelope carried an error without an HTTP status: decodeEnvelopeResult re-wraps the plugin's error message verbatim as fmt.Errorf(\"%s\", message). This is the generic passthrough for plugin-reported failures (login failed, provider error, unsupported operation), so the actionable content is the message text itself, prefixed by whatever context callPlugin adds.","triggerScenarios":"Plugin method returns ok=false with Error{Message: \"...\"} and HTTPStatus <= 0 — e.g. a plugin-side OAuth exchange failing, or the plugin rejecting unsupported parameters.","commonSituations":"Provider-side auth failures surfaced through a plugin; plugin feature not implemented; any business error raised inside plugin code that is not mapped to an HTTP status.","solutions":["Read the propagated message — it is the plugin's own explanation of the failure","Fix the underlying condition in the plugin or its configuration (credentials, provider settings)","If you maintain the plugin and the error should surface as an HTTP status, set Error.HTTPStatus so hosts can map it to a response code"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"out, err := callPlugin[T](ctx, client, method, req)\nif err != nil {\n    var rpcErr rpcPluginError\n    if errors.As(err, &rpcErr) || true {\n        log.WithError(err).Error(\"plugin reported failure\") // message is plugin-authored; surface it to the user verbatim\n    }\n    return err\n}","preventionTips":["Surface the propagated message to operators instead of masking it with a generic wrapper","In plugin code, always set a human-readable Error.Message so this path is diagnosable"],"tags":["rpc","plugin","error-passthrough"],"backgroundTag":null,"analyzedSha":"78f0c4079e3e6273d65d03b5549cffc898703264","analyzedAt":"2026-08-15T12:26:37.444Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}