egametang/ET · error · RpcException

location add failed key: {key} actorId: {actorId} error: {re

Error message

location add failed key: {key} actorId: {actorId} error: {response.Message}

What it means

Thrown by LocationProxyComponent.Add when the RPC response from the primary Location scene returns a non-success error code. This is a wrapper exception that re-throws the server-side error code with additional proxy-level context (key and actorId). The actual root cause is in response.Error / response.Message, which could be any of the server-side errors (ERR_LocationAlreadyLocked, ERR_LocationFollowerRejected, etc.).

Source

Thrown at Packages/cn.etetet.actorlocation/Scripts/Hotfix/Server/LocationProxyComponentSystem.cs:216

                    {
                        throw new Exception("location proxy root disposed");
                    }
                }
            }
        }

        public static async ETTask Add(this LocationProxyComponent self, int type, long key, ActorId actorId)
        {
            Log.Info($"location proxy add {key}, {actorId} {self.GetSingleton<TimeInfo>().ServerNow()}");
            ObjectAddRequest request = ObjectAddRequest.Create();
            request.Type = type;
            request.Key = key;
            request.ActorId = actorId;

            ObjectAddResponse response = (ObjectAddResponse)await self.CallPrimaryWithRetry(key, request);
            if (response.Error != ErrorCode.ERR_Success)
            {
                throw new RpcException(response.Error,
                    $"location add failed key: {key} actorId: {actorId} error: {response.Message}");
            }
        }

        public static async ETTask<long> LockWithToken(this LocationProxyComponent self, int type, long key, ActorId actorId,
            int time = 60000)
        {
            Log.Info($"location proxy lock {key}, {actorId} {self.GetSingleton<TimeInfo>().ServerNow()}");

            ObjectLockRequest request = ObjectLockRequest.Create();
            request.Type = type;
            request.Key = key;
            request.ActorId = actorId;
            request.Time = time;

            ObjectLockResponse response = (ObjectLockResponse)await self.CallPrimaryWithRetry(key, request);
            if (response.Error != ErrorCode.ERR_Success)
            {

View on GitHub (pinned to 5cab01f7a8)

Solutions

  1. Catch RpcException and inspect e.Error to determine the root cause -- ERR_LocationAlreadyLocked means the key is locked, ERR_LocationPrimaryUnavailable means no primary was reachable after retries.
  2. For ERR_LocationAlreadyLocked, resolve the lock conflict before retrying Add.
  3. For ERR_LocationPrimaryUnavailable/ERR_LocationFollowerRejected after exhaustion, check Location scene availability and topology.
  4. Increase locationRequestRetryTimes if transient failures are expected to last longer.

Example fix

// before -- unhandled proxy Add
await locationProxy.Add(type, key, actorId);

// after -- catch and handle by error code
try
{
    await locationProxy.Add(type, key, actorId);
}
catch (RpcException e) when (e.Error == ErrorCode.ERR_LocationAlreadyLocked)
{
    Log.Warning($"location add blocked by lock: key={key}");
}
catch (RpcException e) when (e.Error == ErrorCode.ERR_LocationPrimaryUnavailable)
{
    Log.Error($"location primary unavailable after retries: key={key}");
    throw;
}
Defensive patterns

Strategy: try-catch

Try / catch

try
{
    await locationProxy.Add(type, key, actorId);
}
catch (RpcException e) when (e.Error == ErrorCode.ERR_LocationAlreadyLocked)
{
    Log.Warning($"Add rejected -- key {key} is locked");
}
catch (RpcException e) when (e.Error == ErrorCode.ERR_LocationPrimaryUnavailable)
{
    Log.Error($"Location primary unavailable after retries: {e.Message}");
    throw;
}

Prevention

When it happens

Trigger: LocationProxyComponent.Add(type, key, actorId) calls CallPrimaryWithRetry, gets a response with response.Error != ERR_Success. CallPrimaryWithRetry already retried up to locationRequestRetryTimes for transient errors (FollowerRejected, PrimaryUnavailable), so this throw means either retries were exhausted or the error is non-transient (e.g. AlreadyLocked).

Common situations: The primary returned ERR_LocationAlreadyLocked (the key is locked); retries exhausted during a primary failover (all 20 retries hit PrimaryUnavailable/FollowerRejected); the primary returned an unexpected internal error.

Related errors


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