{"record":{"id":"d38343b8fca39b5c","repo":"egametang/ET","slug":"rpc-error-request-actor-reply-d38343","errorCode":null,"errorMessage":"Rpc error: request, 注意Actor消息超时，请注意查看是否死锁或者没有reply: actorId: {actorId} {request}, response: {response}","messagePattern":"Rpc error: request, 注意Actor消息超时，请注意查看是否死锁或者没有reply: actorId: (.+?) (.+?), response: (.+?)","errorType":"exception","errorClass":"RpcException","httpStatus":null,"severity":"error","filePath":"Packages/cn.etetet.netinner/Scripts/Hotfix/Server/MessageSenderSystem.cs","lineNumber":68,"sourceCode":"            {\n                response = await self.ProcessInnerSender.Call(actorId.FiberInstanceId, request, timeout, needException);\n            }\n            else\n            {\n                // 发给NetInner纤程\n                A2NetInner_Request a2NetInner_Request = A2NetInner_Request.Create();\n                a2NetInner_Request.ActorId = actorId;\n                a2NetInner_Request.MessageObject = request;\n\n                FiberInstanceId netInnerFiberInstanceId = self.GetSingleton<ProcessFiberAddressSingleton>().Get(SceneType.NetInner);\n                using A2NetInner_Response a2NetInnerResponse = await self.ProcessInnerSender.Call(\n                    netInnerFiberInstanceId, a2NetInner_Request, timeout) as A2NetInner_Response;\n                response = a2NetInnerResponse.MessageObject;\n            }\n\n            if (response.Error == ErrorCode.ERR_MessageTimeout)\n            {\n                throw new RpcException(response.Error, $\"Rpc error: request, 注意Actor消息超时，请注意查看是否死锁或者没有reply: actorId: {actorId} {request}, response: {response}\");\n            }\n            if (needException && ErrorCode.IsRpcNeedThrowException(response.Error))\n            {\n                throw new RpcException(response.Error, $\"Rpc error: actorId: {actorId} {request}, response: {response}\");\n            }\n            return response;\n        }\n    }\n}\n","sourceCodeStart":50,"sourceCodeEnd":78,"githubUrl":"https://github.com/egametang/ET/blob/5cab01f7a8bee5f49f4781eebe9e2b1c6d7ebe0f/Packages/cn.etetet.netinner/Scripts/Hotfix/Server/MessageSenderSystem.cs#L50-L78","documentation":"An Actor RPC returned ErrorCode.ERR_MessageTimeout — the request was sent but no reply arrived within the timeout window. The message explicitly warns about deadlocks or missing reply handlers, the two dominant root causes in the ET actor model. It is thrown as RpcException only when the response error code equals ERR_MessageTimeout.","triggerScenarios":"The handler fiber never calls reply/response on the request; the handler is blocked on a lock or on another synchronous RPC that waits on this fiber (deadlock); the target process crashed or its NetInner fiber is overloaded; the timeout value passed to Call is too short for the workload.","commonSituations":"Two fibers mutually calling each other synchronously (Call) and blocking on both; a request handler that does async work but forgets to send the response; a NetInner fiber whose Update loop is starved by a long-running handler; network latency or packet loss on KCP/TCP inner channels exceeding TIMEOUT_TIME.","solutions":["Search the request's handler for a missing Reply/response send — every IRequest handler must return/send an IResponse.","Look for synchronous Call chains that form a cycle across fibers (A calls B, B calls A) and break them with fire-and-forget Send or by reordering.","Increase the timeout argument if the operation is legitimately long, or move heavy work off the actor fiber.","Check that the target process and its NetInner fiber are alive and not CPU-starved (logs, profiler)."],"exampleFix":"// before — handler does work but never replies\n[FriendClass(typeof(SomeComponent))]\npublic class SomeHandler : MessageHandler<SomeRequest>\n{\n    protected override async ETTask Run(Scene scene, SomeRequest request)\n    {\n        await DoWork();\n        // forgot to reply\n    }\n}\n\n// after — always reply\nprotected override async ETTask Run(Scene scene, SomeRequest request)\n{\n    await DoWork();\n    scene.Fiber().Send(request.ActorId, new SomeResponse { ActorId = request.ActorId });\n}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try { var resp = await sender.Call(actorId, request); }\ncatch (RpcException ex) when (ex.Error == ErrorCode.ERR_MessageTimeout)\n{ Log.Error($\"RPC timeout to {actorId}, possible deadlock: {ex}\"); /* retry or fail fast */ }","preventionTips":["Audit every IRequest handler to ensure it always sends a response.","Avoid synchronous Call cycles across fibers; use Send to break cycles.","Profile NetInner fiber Update loops for starvation from long handlers."],"tags":["et-framework","actor-rpc","timeout","deadlock","network"],"backgroundTag":null,"analyzedSha":"5cab01f7a8bee5f49f4781eebe9e2b1c6d7ebe0f","analyzedAt":"2026-08-13T21:10:40.377Z","schemaVersion":2},"datasetVersion":"2026-08-14T00:17:13.853Z"}