{"record":{"id":"30883b08325219f2","repo":"router-for-me/CLIProxyAPI","slug":"plugin-call-failed","errorCode":null,"errorMessage":"plugin call failed","messagePattern":"plugin call failed","errorType":"http","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/pluginhost/rpc_client.go","lineNumber":315,"sourceCode":"\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})\n}\n","sourceCodeStart":297,"sourceCodeEnd":333,"githubUrl":"https://github.com/router-for-me/CLIProxyAPI/blob/78f0c4079e3e6273d65d03b5549cffc898703264/internal/pluginhost/rpc_client.go#L297-L333","documentation":"The plugin returned an envelope with ok=false but no Error object at all, so decodeEnvelopeResult can only report the generic 'plugin call failed'. The plugin failed without explaining why — typically a stubbed method, a defensive false return, or a version that omits error details.","triggerScenarios":"Plugin handler returns an envelope {ok:false} with a nil/absent error field for any method invoked via callPlugin.","commonSituations":"Plugin method not implemented (returns bare failure); plugin built from a skeleton/template; error-construction path in the plugin dropped the Error field.","solutions":["Check the plugin's logs/stderr for the real failure reason at that timestamp","If you own the plugin, always populate Envelope.Error.Message on failure","Verify the plugin implements the method being called (version/feature check)"],"exampleFix":"// plugin: before\nreturn marshalRPCEnvelope(nil) // with ok=false and no error\n\n// plugin: after\nreturn pluginabi.NewErrorEnvelope(fmt.Errorf(\"translate: unsupported model %s\", model))","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"out, err := callPlugin[T](ctx, client, method, req)\nif err != nil && err.Error() == \"plugin call failed\" {\n    log.Errorf(\"plugin %s failed without detail; check plugin stderr/logs\", method)\n}","preventionTips":["When writing plugins, never return ok=false without populating Envelope.Error.Message","Always run plugins with stderr captured to a log sink so detail-less failures remain diagnosable"],"tags":["rpc","plugin","observability"],"backgroundTag":null,"analyzedSha":"78f0c4079e3e6273d65d03b5549cffc898703264","analyzedAt":"2026-08-15T12:26:37.444Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}