{"record":{"id":"e9a7f00906470bd0","repo":"egametang/ET","slug":"err-locationlocknotfound","errorCode":"ERR_LocationLockNotFound","errorMessage":"location unlock not found type: {locationType} key: {key} oldActorId: {oldActorId}","messagePattern":"location unlock not found type: (.+?) key: (.+?) oldActorId: (.+?)","errorType":"exception","errorClass":"RpcException","httpStatus":null,"severity":"error","filePath":"Packages/cn.etetet.actorlocation/Scripts/Hotfix/Server/LocationComponentSystem.cs","lineNumber":480,"sourceCode":"        }\n\n        public static async ETTask UnLock(this LocationComponent self, int locationType, long key, ActorId oldActorId, ActorId newActorId,\n        long lockToken)\n        {\n            EntityRef<LocationComponent> selfRef = self;\n            using (await self.Root().CoroutineLockComponent.Wait(CoroutineLockType.Location, key))\n            {\n                self = selfRef;\n                if (self == null)\n                {\n                    return;\n                }\n\n                LocationInfo locationInfo = await self.GetOrLoadInfo(key);\n                self = selfRef;\n                if (self == null || locationInfo == null || !TryGetRouteState(locationInfo, locationType, out LocationTypeState state))\n                {\n                    throw new RpcException(ErrorCode.ERR_LocationLockNotFound,\n                        $\"location unlock not found type: {locationType} key: {key} oldActorId: {oldActorId}\");\n                }\n\n                Dictionary<int, LocationTypeState> snapshot = CreateStatesSnapshot(locationInfo);\n                if (self.IsExpiredLock(state))\n                {\n                    state.LockToken = default;\n                    state.LockExpireTime = default;\n                    locationInfo.TypeStates[locationType] = state;\n\n                    try\n                    {\n                        await self.SaveInfoToDB(locationInfo);\n                    }\n                    catch\n                    {\n                        self = selfRef;\n                        if (self != null)","sourceCodeStart":462,"sourceCodeEnd":498,"githubUrl":"https://github.com/egametang/ET/blob/5cab01f7a8bee5f49f4781eebe9e2b1c6d7ebe0f/Packages/cn.etetet.actorlocation/Scripts/Hotfix/Server/LocationComponentSystem.cs#L462-L498","documentation":"TargetSelectHandler<Node>.Handle throws when the TargetSelector node passed in is not assignable to the handler's generic Node type. The dispatcher registers handlers keyed by node Type and, on dispatch, walks node.GetType() up through BaseType to find a match, so a correct registration should never deliver a mismatched node to a handler.","triggerScenarios":"A handler's Handle is invoked with a node whose runtime type is not the registered Node type — happens only if the handlers dictionary was populated for the wrong key, GetNodeType() returns a type inconsistent with the generic constraint, or a node subclass has no dedicated handler and the BaseType walk landed on a handler registered for a sibling type.","commonSituations":"Two handlers registered under keys in the same inheritance chain causing an ambiguous/incorrect lookup; a TargetSelector subclass added without a matching handler; handler generic Node parameter changed during refactor but GetNodeType unchanged.","solutions":["Ensure each TargetSelector subtype has exactly one handler whose generic Node type equals (or is a base of) the node type it should receive.","Confirm GetNodeType() returns typeof(Node) and that this matches the key used in Register (handlers.TryAdd(nodeType, ...)).","Check for duplicate or conflicting registrations across the TargetSelector hierarchy in the handlers dictionary.","Add a dedicated handler for the specific node subtype that is currently mismatching."],"exampleFix":"// before\npublic async ETTask<int> Handle(TargetSelector node, ...)\n{\n    if (node is not Node c)\n    {\n        throw new Exception($\"type mismatch: {node.GetType().FullName} to {typeof(Node).Name}\");\n    }\n    return await this.Run(c, unit, spellConfig);\n}\n\n// after\npublic async ETTask<int> Handle(TargetSelector node, ...)\n{\n    if (node is not Node c)\n    {\n        Log.Error($\"node type mismatch: {node.GetType().FullName} expected {typeof(Node).Name}; returning 0\");\n        return 0;\n    }\n    return await this.Run(c, unit, spellConfig);\n}","handlingStrategy":"type-guard","validationCode":null,"typeGuard":"static bool IsHandlerFor(ITargetSelectHandler handler, TargetSelector node)\n{\n    return handler.GetNodeType().IsAssignableFrom(node.GetType());\n}","tryCatchPattern":"try\n{\n    return await handler.Handle(node, unit, spellConfig);\n}\ncatch (Exception ex) when (ex.Message.Contains(\"type mismatch\"))\n{\n    Log.Error($\"node/handler type mismatch: {node.GetType().FullName} vs {handler.GetNodeType().Name}\");\n    return 0;\n}","preventionTips":["Register exactly one handler per TargetSelector type, keyed by GetNodeType(), to keep dispatch unambiguous.","Keep GetNodeType() returning typeof(Node) consistent with the generic constraint on every handler.","Add a handler for each new TargetSelector subclass instead of relying on a base-type match."],"tags":["spell","target-select","dispatch","type-mismatch","behavior-tree"],"backgroundTag":null,"analyzedSha":"5cab01f7a8bee5f49f4781eebe9e2b1c6d7ebe0f","analyzedAt":"2026-08-13T21:10:40.377Z","schemaVersion":2},"datasetVersion":"2026-08-14T00:17:13.853Z"}