egametang/ET · error · Exception

actor id is 0: {message}

Error message

actor id is 0: {message}

What it means

ProcessOuterSender.SendInner refuses to send a message when actorId is default (zero). ProcessOuterSender is the cross-process outer channel — unlike the inner sender it never routes locally, so a zero id cannot be resolved to any session. This is a hard precondition check before a Session lookup and Send.

Source

Thrown at Packages/cn.etetet.netinner/Scripts/Hotfix/Server/ProcessOuterSenderSystem.cs:194

            if (self.NeedException && ErrorCode.IsRpcNeedThrowException(response.Error))
            {
                self.SetException(new RpcException(response.Error, $"Rpc error: actorId: {self.ActorId} request: {self.RequestType.FullName}, response: {response}"));
                return;
            }

            self.SetResult(response);
        }

        public static void Send(this ProcessOuterSender self, ActorId actorId, IMessage message)
        {
            self.SendInner(actorId, message as MessageObject);
        }

        private static void SendInner(this ProcessOuterSender self, ActorId actorId, MessageObject message)
        {
            if (actorId == default)
            {
                throw new Exception($"actor id is 0: {message}");
            }
            
            if (message == null)
            {
                throw new Exception($"message is null");
            }

            // 如果发向同一个进程,则报错
            if (actorId.Address == self.GetSingleton<AddressSingleton>().InnerAddress)
            {
                throw new Exception($"actor is the same process: {actorId}");
            }
            Session session = self.Get(actorId.Address);
            session.Send(actorId.FiberInstanceId, message);
        }

        private static int GetRpcId(this ProcessOuterSender self)
        {

View on GitHub (pinned to 5cab01f7a8)

Solutions

  1. Log the actorId and its source (which config entry or look-up produced it) at the call site.
  2. Confirm the target process is declared in the process/address configuration and its address is registered.
  3. Guard the send with an actorId == default check and skip or queue the message.

Example fix

// before
outerSender.Send(actorId, msg);

// after
if (actorId == default)
{
    Log.Error($"cannot Send, actorId is zero for {msg}");
    return;
}
outerSender.Send(actorId, msg);
Defensive patterns

Strategy: validation

Validate before calling

if (actorId == default) { Log.Error($"cannot Send, actorId is zero for {message}"); return; }

Prevention

When it happens

Trigger: Calling ProcessOuterSender.Send(actorId, message) with an ActorId that was never populated; resolving an address from a config/map that returned default for an unknown process.

Common situations: The caller assumed an outer process address exists but it was not configured in the start config; the ActorId field on an entity/component was not initialized before a send; a message was routed based on a player/guild id whose process mapping is missing.

Related errors


AI-assisted analysis of egametang/ET@5cab01f7a8 (2026-08-13). Data as JSON: /api/errors/44826c123852bcbe. Report an issue: GitHub.