{"record":{"id":"719ab141f9a6eb4c","repo":"egametang/ET","slug":"location-remove-failed-key-key-expectedactorid","errorCode":null,"errorMessage":"location remove failed key: {key} expectedActorId: {expectedActorId} error: {response.Message}","messagePattern":"location remove failed key: (.+?) expectedActorId: (.+?) error: (.+?)","errorType":"exception","errorClass":"RpcException","httpStatus":null,"severity":"error","filePath":"Packages/cn.etetet.actorlocation/Scripts/Hotfix/Server/LocationProxyComponentSystem.cs","lineNumber":357,"sourceCode":"\n        public static async ETTask Remove(this LocationProxyComponent self, int type, long key)\n        {\n            await self.Remove(type, key, default);\n        }\n\n        public static async ETTask Remove(this LocationProxyComponent self, int type, long key, ActorId expectedActorId)\n        {\n            Log.Info($\"location proxy remove {key}, {self.GetSingleton<TimeInfo>().ServerNow()}\");\n\n            ObjectRemoveRequest request = ObjectRemoveRequest.Create();\n            request.Type = type;\n            request.Key = key;\n            request.ExpectedActorId = expectedActorId;\n\n            ObjectRemoveResponse response = (ObjectRemoveResponse)await self.CallPrimaryWithRetry(key, request);\n            if (response.Error != ErrorCode.ERR_Success)\n            {\n                throw new RpcException(response.Error,\n                    $\"location remove failed key: {key} expectedActorId: {expectedActorId} error: {response.Message}\");\n            }\n        }\n\n        public static async ETTask<ActorId> Get(this LocationProxyComponent self, int type, long key)\n        {\n            if (key == 0)\n            {\n                throw new Exception(\"get location key 0\");\n            }\n\n            EntityRef<LocationProxyComponent> selfRef = self;\n            int retryCount = 0;\n            while (true)\n            {\n                self = selfRef;\n                if (self == null)\n                {","sourceCodeStart":339,"sourceCodeEnd":375,"githubUrl":"https://github.com/egametang/ET/blob/5cab01f7a8bee5f49f4781eebe9e2b1c6d7ebe0f/Packages/cn.etetet.actorlocation/Scripts/Hotfix/Server/LocationProxyComponentSystem.cs#L339-L375","documentation":"Remove sent an ObjectRemoveRequest with an optional expectedActorId guard to the primary location server via CallPrimaryWithRetry. The server returned a non-success error, typically because expectedActorId didn't match the currently registered ActorId (concurrent modification) or because of an internal server error. The RpcException wraps the server's error code and message.","triggerScenarios":"Remove(type, key, expectedActorId) where expectedActorId doesn't match the server's current mapping for that key — another process already changed it. Also fires on server-side database errors or corruption.","commonSituations":"Calling RemoveLocation after an actor migrated and re-registered with a new ActorId. Race between actor migration and explicit cleanup. RemoveLocation called during server failover when the primary is in an inconsistent state.","solutions":["Call Remove(type, key) without expectedActorId (it passes default) to skip the ownership guard and remove unconditionally.","Re-query the current ActorId via Get(type, key) before calling Remove with expectedActorId, to avoid stale expectations.","Wrap cleanup removes in try-catch and suppress the error if the remove is best-effort (the entity is already gone).","Ensure Remove is not called concurrently with actor migration for the same key."],"exampleFix":"// before\nawait proxy.Remove(type, key, expectedActorId); // throws if ActorId changed\n\n// after (unconditional cleanup)\nawait proxy.Remove(type, key); // passes expectedActorId = default, skips guard","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"// For best-effort cleanup, catch and suppress:\ntry\n{\n    await proxy.Remove(type, key, expectedActorId);\n}\ncatch (RpcException e)\n{\n    Log.Warning($\"location remove failed for {key}, likely already removed: {e.Message}\");\n    // acceptable for cleanup paths\n}","preventionTips":["Use Remove(type, key) without expectedActorId for unconditional cleanup.","Don't call Remove concurrently with actor migration for the same key.","For idempotent cleanup, treat remove failures as non-fatal."],"tags":["location","rpc","concurrent-modification","remove"],"backgroundTag":null,"analyzedSha":"5cab01f7a8bee5f49f4781eebe9e2b1c6d7ebe0f","analyzedAt":"2026-08-13T21:10:40.377Z","schemaVersion":2},"datasetVersion":"2026-08-14T00:17:13.853Z"}