{"record":{"id":"829ef57d57e1953a","repo":"egametang/ET","slug":"actor-id-is-0-irequest","errorCode":null,"errorMessage":"actor id is 0: {iRequest}","messagePattern":"actor id is 0: (.+?)","errorType":"exception","errorClass":"Exception","httpStatus":null,"severity":"error","filePath":"Packages/cn.etetet.netinner/Scripts/Hotfix/Server/ProcessOuterSenderSystem.cs","lineNumber":220,"sourceCode":"            // 如果发向同一个进程，则报错\n            if (actorId.Address == self.GetSingleton<AddressSingleton>().InnerAddress)\n            {\n                throw new Exception($\"actor is the same process: {actorId}\");\n            }\n            Session session = self.Get(actorId.Address);\n            session.Send(actorId.FiberInstanceId, message);\n        }\n\n        private static int GetRpcId(this ProcessOuterSender self)\n        {\n            return ++self.RpcId;\n        }\n\n        public static async ETTask<IResponse> Call(this ProcessOuterSender self, ActorId actorId, IRequest iRequest, bool needException = true)\n        {\n            if (actorId == default)\n            {\n                throw new Exception($\"actor id is 0: {iRequest}\");\n            }\n            Fiber fiber = self.Fiber();\n            \n            int rpcId = self.GetRpcId();\n\n            iRequest.RpcId = rpcId;\n\n            Type requestType = iRequest.GetType();\n            MessageSenderStruct messageSenderStruct = new(actorId, requestType, needException);\n            self.requestCallback.Add(rpcId, messageSenderStruct);\n            self.SendInner(actorId, iRequest as MessageObject);\n            EntityRef<ProcessOuterSender> selfRef = self;\n\n            async ETTask Timeout()\n            {\n                await fiber.Root.TimerComponent.WaitAsync(ProcessOuterSender.TIMEOUT_TIME);\n                self = selfRef;\n                if (!self.requestCallback.Remove(rpcId, out MessageSenderStruct action))","sourceCodeStart":202,"sourceCodeEnd":238,"githubUrl":"https://github.com/egametang/ET/blob/5cab01f7a8bee5f49f4781eebe9e2b1c6d7ebe0f/Packages/cn.etetet.netinner/Scripts/Hotfix/Server/ProcessOuterSenderSystem.cs#L202-L238","documentation":"ProcessOuterSender.Call (the RPC variant) rejects a default/zero ActorId before setting up the rpcId callback and sending. Same precondition as the Send path but on the request/response flow — a zero id means there is no remote process to receive the RPC.","triggerScenarios":"Calling ProcessOuterSender.Call(actorId, iRequest) where actorId is default; the address look-up for the target process returned nothing.","commonSituations":"The target process is not configured or not yet started; a player/entity ActorId was not initialized before an RPC; single-process dev mode calling outer RPC with no remote process configured.","solutions":["Resolve and validate the target ActorId (non-zero, address registered) before calling Call.","Ensure the destination process is in the start config and its outer address is known.","Guard with a default check and degrade gracefully (return a failure response) instead of throwing."],"exampleFix":"// before\nvar resp = await outerSender.Call(actorId, request);\n\n// after\nif (actorId == default)\n{\n    return new SomeResponse { Error = ErrorCode.ERR_ActorIdInvalid };\n}\nvar resp = await outerSender.Call(actorId, request);","handlingStrategy":"validation","validationCode":"if (actorId == default) { return new SomeResponse { Error = ErrorCode.ERR_ActorIdInvalid }; }","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Guard Call with a default check and return a typed failure response.","Ensure target process addresses are registered before RPC traffic.","Log the ActorId source when it is zero to find config gaps."],"tags":["et-framework","actor-rpc","validation","network"],"backgroundTag":null,"analyzedSha":"5cab01f7a8bee5f49f4781eebe9e2b1c6d7ebe0f","analyzedAt":"2026-08-13T21:10:40.377Z","schemaVersion":2},"datasetVersion":"2026-08-14T00:17:13.853Z"}