{"record":{"id":"8fcb6a2e5ce2409e","repo":"egametang/ET","slug":"location-get-retry-exceeded-key-key-retry-ret","errorCode":null,"errorMessage":"location get retry exceeded key: {key} retry: {retryCount}/{self.locationRequestRetryTimes}","messagePattern":"location get retry exceeded key: (.+?) retry: (.+?)/(.+?)","errorType":"exception","errorClass":"RpcException","httpStatus":null,"severity":"warning","filePath":"Packages/cn.etetet.actorlocation/Scripts/Hotfix/Server/LocationProxyComponentSystem.cs","lineNumber":400,"sourceCode":"\n                ObjectGetResponse response = (ObjectGetResponse)await self.CallPrimaryWithRetry(key, request);\n                if (response.Error == ErrorCode.ERR_Success)\n                {\n                    return response.ActorId;\n                }\n\n                if (response.Error == ErrorCode.ERR_LocationGetRetry)\n                {\n                    ++retryCount;\n                    self = selfRef;\n                    if (self == null)\n                    {\n                        throw new Exception(\"location proxy disposed\");\n                    }\n\n                    if (retryCount >= self.locationRequestRetryTimes)\n                    {\n                        throw new RpcException(response.Error,\n                            $\"location get retry exceeded key: {key} retry: {retryCount}/{self.locationRequestRetryTimes}\");\n                    }\n\n                    int delayMs = self.locationRequestRetryIntervalMs * retryCount;\n                    if (delayMs <= 0)\n                    {\n                        delayMs = 1;\n                    }\n\n                    Scene root = self.Root();\n                    EntityRef<Scene> rootRef = root;\n                    await root.TimerComponent.WaitAsync(delayMs);\n                    root = rootRef;\n                    if (root == null)\n                    {\n                        throw new Exception(\"location proxy root disposed\");\n                    }\n                    continue;","sourceCodeStart":382,"sourceCodeEnd":418,"githubUrl":"https://github.com/egametang/ET/blob/5cab01f7a8bee5f49f4781eebe9e2b1c6d7ebe0f/Packages/cn.etetet.actorlocation/Scripts/Hotfix/Server/LocationProxyComponentSystem.cs#L382-L418","documentation":"Get received ERR_LocationGetRetry from the location server, meaning the requested key is currently locked (the entity is being transferred between server processes). The proxy retried with escalating backoff (delay = locationRequestRetryIntervalMs * retryCount) up to locationRequestRetryTimes (default 20, interval default 100ms). All retries returned LocationGetRetry, so the lock was never released within the total retry window.","triggerScenarios":"Get(type, key) while another process holds a location lock on the same key for longer than the cumulative retry window (~20 retries with escalating delays). The lock owner hasn't called UnLock within the retry budget.","commonSituations":"Actor migration/transfer taking longer than expected due to slow serialization or network. A lock that was acquired but never released because the locking process crashed before calling UnLock. Slow database causing lock operations to stall. locationRequestRetryTimes set too low for the deployment.","solutions":["Increase locationRequestRetryTimes on the LocationProxyComponent (set in Awake, default 20) for environments with slow transfers.","Investigate why the lock is held so long — check for crashed processes that acquired location locks without releasing them.","Catch RpcException with ERR_LocationGetRetry and implement a higher-level retry with longer backoff or circuit-breaking.","Reduce the lock timeout in LockWithToken so orphaned locks expire faster."],"exampleFix":"// before — default retry times (20) may be too few\n// proxy.locationRequestRetryTimes = 20 (set in Awake)\nActorId id = await proxy.Get(type, key); // throws after 20 retries\n\n// after — increase retry budget\npublic override void Awake(LocationProxyComponent self)\n{\n    self.locationRequestRetryTimes = 50;\n    self.locationRequestRetryIntervalMs = 200;\n}","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"// Catch at the application level and implement longer backoff retry:\nint appRetry = 0;\nwhile (true)\n{\n    try\n    {\n        ActorId id = await proxy.Get(type, key);\n        return id;\n    }\n    catch (RpcException e) when (e.Error == ErrorCode.ERR_LocationGetRetry)\n    {\n        if (++appRetry > 5)\n            throw;\n        await root.TimerComponent.WaitAsync(2000 * appRetry);\n    }\n}","preventionTips":["Increase locationRequestRetryTimes for slow-transfer environments.","Monitor for stuck location locks — a lock held beyond its timeout indicates a crashed process.","Reduce lock timeout so orphaned locks expire faster, unblocking Get callers.","Use UnLockWithRetry to ensure locks are always released even after transient errors."],"tags":["location","lock-contention","retry-exhausted","transient","actor-transfer"],"backgroundTag":null,"analyzedSha":"5cab01f7a8bee5f49f4781eebe9e2b1c6d7ebe0f","analyzedAt":"2026-08-13T21:10:40.377Z","schemaVersion":2},"datasetVersion":"2026-08-14T00:17:13.853Z"}