egametang/ET · error · RpcException
ERR_MessageTimeout
ERR_MessageTimeout
Error message
{message} What it means
In SendInner (fire-and-forget IMessage path), after awaiting the CoroutineLock for the entityId, the MessageLocationSender was found null via the EntityRef. This means the sender child entity was disposed during the await on the coroutine lock — either by the 10s Check timer (which removes senders idle > TIMEOUT_TIME), by explicit Remove(), or by parent entity disposal.
Source
Thrown at Packages/cn.etetet.actorlocation/Scripts/Hotfix/Server/MessageLocationSenderComponentSystem.cs:116
public static async ETTask SendAsync(this MessageLocationSenderOneType self, long entityId, IMessage message)
{
await self.SendInner(entityId, message);
}
private static async ETTask SendInner(this MessageLocationSenderOneType self, long entityId, IMessage message)
{
EntityRef<MessageLocationSenderOneType> selfRef = self;
MessageLocationSender messageLocationSender = self.GetOrCreate(entityId);
EntityRef<MessageLocationSender> messageLocationSenderRef = messageLocationSender;
Scene root = self.Root();
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}");View on GitHub (pinned to 5cab01f7a8)
Solutions
- Catch RpcException with ERR_MessageTimeout and re-send the message — GetOrCreate will recreate a fresh sender.
- Use SendAsync instead of Send if you need to await and handle the error at the call site.
- Reduce coroutine lock contention on the same entityId to shorten the lock-wait window.
- Ensure the entity is actively communicating so the sender doesn't time out.
Example fix
// before — fire-and-forget, error is swallowed
senderOneType.Send(entityId, message);
// after — await and retry on timeout
try
{
await senderOneType.SendAsync(entityId, message);
}
catch (RpcException e) when (e.Error == ErrorCode.ERR_MessageTimeout)
{
await senderOneType.SendAsync(entityId, message); // GetOrCreate makes a new sender
} Defensive patterns
Strategy: try-catch
Try / catch
// For fire-and-forget Send, use SendAsync and retry on timeout:
try
{
await senderOneType.SendAsync(entityId, message);
}
catch (RpcException e) when (e.Error == ErrorCode.ERR_MessageTimeout)
{
// Sender was disposed during lock wait — recreate and retry
await senderOneType.SendAsync(entityId, message);
} Prevention
- Use SendAsync instead of fire-and-forget Send when you need error recovery.
- Reduce coroutine lock contention on the same entityId.
- Keep the entity actively communicating so the sender doesn't time out.
- Increase TIMEOUT_TIME if idle gaps are expected.
When it happens
Trigger: MessageLocationSenderOneType.Send(entityId, message) where the MessageLocationSender child is removed (Check timer expiry or explicit Remove) during the await on CoroutineLockType.MessageLocationSender for that entityId.
Common situations: The entity was idle long enough for TIMEOUT_TIME to elapse. Another coroutine explicitly removed the sender (disconnect/reconnect to a different Gate). High contention on the same entityId's coroutine lock causing long waits that exceed the sender's lifetime.
Related errors
- ERR_ActorLocationSenderTimeout2
- 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/a86f8a53aca8e5eb.
Report an issue: GitHub.