{"record":{"id":"f0368f24f3c481c9","repo":"egametang/ET","slug":"err-locationlocktokenmismatch-f0368f","errorCode":"ERR_LocationLockTokenMismatch","errorMessage":"location unlock requires lock token key: {key} oldActorId: {oldActorId} newActorId: {newActorId}","messagePattern":"location unlock requires lock token key: (.+?) oldActorId: (.+?) newActorId: (.+?)","errorType":"exception","errorClass":"RpcException","httpStatus":null,"severity":"error","filePath":"Packages/cn.etetet.actorlocation/Scripts/Hotfix/Server/LocationProxyComponentSystem.cs","lineNumber":253,"sourceCode":"                throw new RpcException(response.Error,\n                    $\"location lock failed key: {key} actorId: {actorId} error: {response.Message}\");\n            }\n\n            return response.LockToken;\n        }\n\n        [Obsolete(\"Use LockWithToken and pass the returned token to UnLock.\", true)]\n        public static async ETTask Lock(this LocationProxyComponent self, int type, long key, ActorId actorId,\n            int time = 60000)\n        {\n            await self.LockWithToken(type, key, actorId, time);\n        }\n\n        [Obsolete(\"Use UnLock overload with lockToken.\", true)]\n        public static ETTask UnLock(this LocationProxyComponent self, int type, long key, ActorId oldActorId,\n            ActorId newActorId)\n        {\n            throw new RpcException(ErrorCode.ERR_LocationLockTokenMismatch,\n                $\"location unlock requires lock token key: {key} oldActorId: {oldActorId} newActorId: {newActorId}\");\n        }\n\n        public static async ETTask UnLock(this LocationProxyComponent self, int type, long key, ActorId oldActorId,\n            ActorId newActorId, long lockToken)\n        {\n            Log.Info(\n                $\"location proxy unlock {key}, {newActorId} lockToken: {lockToken} {self.GetSingleton<TimeInfo>().ServerNow()}\");\n\n            ObjectUnLockRequest request = ObjectUnLockRequest.Create();\n            request.Type = type;\n            request.Key = key;\n            request.OldActorId = oldActorId;\n            request.NewActorId = newActorId;\n            request.LockToken = lockToken;\n\n            ObjectUnLockResponse response = (ObjectUnLockResponse)await self.CallPrimaryWithRetry(key, request);\n            if (response.Error != ErrorCode.ERR_Success)","sourceCodeStart":235,"sourceCodeEnd":271,"githubUrl":"https://github.com/egametang/ET/blob/5cab01f7a8bee5f49f4781eebe9e2b1c6d7ebe0f/Packages/cn.etetet.actorlocation/Scripts/Hotfix/Server/LocationProxyComponentSystem.cs#L235-L271","documentation":"Thrown by the obsolete UnLock overload that lacks a lockToken parameter. The location-lock API was migrated to a token-based scheme: LockWithToken returns a long token that must be passed to UnLock. The [Obsolete(..., true)] attribute makes the C# compiler reject any call at compile time (error CS0619); this runtime throw exists as a safety net for reflection-based or dynamically dispatched calls that bypass the compiler check.","triggerScenarios":"Invoking UnLock(type, key, oldActorId, newActorId) — the 4-parameter overload without lockToken — via reflection (MethodInfo.Invoke), dynamic dispatch, or a build that suppresses CS0619. Under normal compilation this method is unreachable.","commonSituations":"Upgrading from a pre-token version of cn.etetet.actorlocation where UnLock did not require a token. Test harnesses or serialization frameworks that invoke methods by signature via reflection. Code that uses #pragma warning disable CS0619 around unlock calls.","solutions":["Replace Lock(...) with LockWithToken(type, key, actorId, time) and capture the returned long lockToken.","Replace the 4-parameter UnLock(...) call with the 5-parameter UnLock(type, key, oldActorId, newActorId, lockToken) overload, passing the captured token.","Remove any #pragma warning disable CS0619 / CS0618 directives around lock/unlock code so the compiler catches the obsolete overload.","If invoking via reflection, update the MethodInfo lookup to select the 5-parameter overload."],"exampleFix":"// before\nlocationProxy.Lock(type, key, actorId);\n// ... later ...\nlocationProxy.UnLock(type, key, oldActorId, newActorId); // CS0619 or runtime throw\n\n// after\nlong lockToken = await locationProxy.LockWithToken(type, key, actorId);\n// ... later ...\nawait locationProxy.UnLock(type, key, oldActorId, newActorId, lockToken);","handlingStrategy":"validation","validationCode":"// Ensure you use the token-based API exclusively.\n// The compiler enforces this via [Obsolete(..., true)] = CS0619.\n// If using reflection, explicitly select the 5-parameter overload:\n\nvar method = typeof(LocationProxyComponentSystem)\n    .GetMethods()\n    .First(m => m.Name == \"UnLock\" && m.GetParameters().Length == 6); // self + 5 params incl. lockToken\n\n// Verify lockToken is non-zero before unlocking:\nif (lockToken == 0)\n{\n    throw new InvalidOperationException(\"lockToken must come from LockWithToken\");\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always pair LockWithToken → UnLock(5-param, lockToken). Never call the 4-param UnLock.","Do not suppress CS0619 around lock/unlock code.","Store the lockToken in a field or pass it through the call chain; never discard it.","The token is the only proof of lock ownership — treat it like a capability."],"tags":["api-misuse","location-lock","obsolete","token"],"backgroundTag":null,"analyzedSha":"5cab01f7a8bee5f49f4781eebe9e2b1c6d7ebe0f","analyzedAt":"2026-08-13T21:10:40.377Z","schemaVersion":2},"datasetVersion":"2026-08-14T00:17:13.853Z"}