egametang/ET · error · RpcException
ERR_ActorLocationSenderTimeout2
ERR_ActorLocationSenderTimeout2
Error message
{message} What it means
In SendInner, the cached ActorId was default, so a LocationProxyComponent.Get was issued to resolve the target. After that async location lookup returned, the MessageLocationSender was re-acquired from EntityRef and found null — the sender was disposed during the location Get round-trip. The message can no longer be delivered through this sender.
Source
Thrown at Packages/cn.etetet.actorlocation/Scripts/Hotfix/Server/MessageLocationSenderComponentSystem.cs:127
EntityRef<Scene> rootRef = root;
using (await root.CoroutineLockComponent.Wait(CoroutineLockType.MessageLocationSender, entityId))
{
messageLocationSender = messageLocationSenderRef;
if (messageLocationSender == null)
{
throw new RpcException(ErrorCode.ERR_MessageTimeout, $"{message}");
}
if (messageLocationSender.ActorId == default)
{
root = rootRef;
self = selfRef;
ActorId actorId = await root.GetComponent<LocationProxyComponent>().Get((int)self.Id, messageLocationSender.Id);
messageLocationSender = messageLocationSenderRef;
if (messageLocationSender == null)
{
throw new RpcException(ErrorCode.ERR_ActorLocationSenderTimeout2, $"{message}");
}
if (actorId == default)
{
self = selfRef;
self?.Remove(entityId);
throw new RpcException(ErrorCode.ERR_NotFoundActor, $"{message}");
}
messageLocationSender.ActorId = actorId;
}
messageLocationSender = messageLocationSenderRef;
messageLocationSender.LastSendOrRecvTime = messageLocationSender.GetSingleton<TimeInfo>().ServerNow();
root = rootRef;
root.GetComponent<MessageSender>().Send(messageLocationSender.ActorId, message);
}
}View on GitHub (pinned to 5cab01f7a8)
Solutions
- Catch RpcException with ERR_ActorLocationSenderTimeout2 and re-send — GetOrCreate will make a new sender.
- Use SendAsync to await and handle disposal errors at the call site.
- Reduce location server latency to narrow the disposal window.
- Keep the sender's ActorId populated (send frequently) to avoid triggering location lookups.
Defensive patterns
Strategy: try-catch
Try / catch
try
{
await senderOneType.SendAsync(entityId, message);
}
catch (RpcException e) when (e.Error == ErrorCode.ERR_ActorLocationSenderTimeout2)
{
// Sender disposed during location lookup — retry recreates it
await senderOneType.SendAsync(entityId, message);
} Prevention
- Reduce location server latency to narrow the disposal window.
- Keep the sender's ActorId populated to avoid triggering location lookups.
- Use SendAsync to handle disposal errors at the call site.
When it happens
Trigger: MessageLocationSenderOneType.Send(entityId, message) where messageLocationSender.ActorId is default, triggering a location lookup, and during that async lookup the sender is disposed by the Check timer or explicit Remove.
Common situations: The Check timer fires during the location server round-trip. Location server is slow, widening the disposal window. Another coroutine removes the sender (e.g., player reconnect) during the lookup.
Related errors
- ERR_MessageTimeout
- ERR_ActorLocationSenderTimeout3
- ERR_ActorLocationSenderTimeout4
- ERR_NotFoundActor
- Message: {response.Message} Request: {requestType.FullName}
AI-assisted analysis of egametang/ET@5cab01f7a8 (2026-08-13).
Data as JSON: /api/errors/389e058ce7d6bab1.
Report an issue: GitHub.