egametang/ET · error · RpcException
ERR_LocationAlreadyLocked
ERR_LocationAlreadyLocked
Error message
location add rejected by lock type: {locationType} key: {key} actorId: {currentState.ActorId} lockToken: {currentState.LockToken} What it means
Thrown by ValidateServiceInfo when a ServiceInfo entry in the proxy's local cache is null, has a blank SceneName, or has a default ActorId. It guards SelectServiceActorIdByRouteKey against corrupted cache entries that would break the Sort/CompareServiceInfo or produce a route to a dead ActorId. Wrapped as RpcException(ERR_ServiceDiscoveryOperationFailed), which is NOT in the ERR_ServiceNotFound retry set, so it surfaces immediately.
Source
Thrown at Packages/cn.etetet.actorlocation/Scripts/Hotfix/Server/LocationComponentSystem.cs:275
if (self == null)
{
return;
}
locationInfo ??= self.CreateInfo(key);
Dictionary<int, LocationTypeState> snapshot = CreateStatesSnapshot(locationInfo);
if (TryGetRouteState(locationInfo, locationType, out LocationTypeState currentState)
&& self.IsExpiredLock(currentState))
{
currentState.LockToken = default;
currentState.LockExpireTime = default;
locationInfo.TypeStates[locationType] = currentState;
}
if (TryGetRouteState(locationInfo, locationType, out currentState) && IsLocked(currentState))
{
throw new RpcException(ErrorCode.ERR_LocationAlreadyLocked,
$"location add rejected by lock type: {locationType} key: {key} actorId: {currentState.ActorId} lockToken: {currentState.LockToken}");
}
bool oldExists = TryGetRouteState(locationInfo, locationType, out LocationTypeState oldState);
locationInfo.TypeStates[locationType] = new LocationTypeState { ActorId = actorId };
try
{
await self.SaveInfoToDB(locationInfo);
}
catch
{
self = selfRef;
if (self != null)
{
self.RestoreStates(key, snapshot);
}
View on GitHub (pinned to 5cab01f7a8)
Solutions
- Search registration/update logs for entries with empty SceneName or default ActorId reaching the proxy (see FormatServiceInfo output).
- Ensure all ServiceInfo insertion goes through AddOrUpdateLocalService, whose null/empty/default guard at line 536 must not be bypassed.
- Verify scenes that dispose emit a remove notification (OnServiceChangeRemoveService) so stale EntityRefs are cleaned via RemoveLocalService.
- Clear the proxy's local cache (ClearLocalServices) and let it re-resolve from the master if corruption is suspected.
Example fix
// before
orderedServices.Add(serviceInfo); // ValidateServiceInfo throws on bad entry
// after
if (serviceInfo == null || string.IsNullOrWhiteSpace(serviceInfo.SceneName) || serviceInfo.ActorId == default)
{
Log.Error($"skip invalid local cache entry sceneType: {sceneTypeName}");
continue;
}
orderedServices.Add(serviceInfo); Defensive patterns
Strategy: validation
Validate before calling
foreach (ServiceInfo si in proxy.GetBySceneType(sceneType))
{
if (si == null || string.IsNullOrWhiteSpace(si.SceneName) || si.ActorId == default)
{
Log.Error($"invalid cached ServiceInfo detected for sceneType {sceneType}");
}
} Try / catch
try
{
ActorId id = proxy.ResolveServiceActorIdByRouteKeyOnce(sceneType, sceneTypeName, routeKey);
}
catch (RpcException ex) when (ex.Error == ErrorCode.ERR_ServiceDiscoveryOperationFailed)
{
// local cache corruption; clear and re-resolve
proxy.ClearLocalServices(true);
} Prevention
- Route all ServiceInfo insertion through AddOrUpdateLocalService so the null/empty/default guard is enforced.
- Ensure disposing scenes emit deregister notifications so stale EntityRef<ServiceInfo> entries are removed.
- Periodically validate the SceneNameServices cache for dangling EntityRefs in dev/test.
When it happens
Trigger: The SceneNameServices cache contains a malformed ServiceInfo: a stale EntityRef<ServiceInfo> whose target was disposed (resolves to null), a ServiceInfo whose SceneName was cleared, or one whose ActorId is default. AddOrUpdateLocalService filters these out at line 536, so a hit means that filter was bypassed or an EntityRef went stale after insertion.
Common situations: Target scene disposed without sending a deregister notification, leaving a dangling EntityRef; concurrent mutation of SceneNameServices racing with iteration; partial deserialization after a reload; a code path adding ServiceInfo directly to SceneNameServices instead of through AddOrUpdateLocalService.
Related errors
- location db manager not found scene: {root.Name}
- entity is disposed, instanceid == 0!
- process fiber address not found, sceneType: {sceneType}
- unsupported config type: {configProcessAttribute.ConfigType}
- actor is the same process: {actorId}
AI-assisted analysis of egametang/ET@5cab01f7a8 (2026-08-13).
Data as JSON: /api/errors/c9fa954a3ba35d9a.
Report an issue: GitHub.