egametang/ET · error · Exception

service discovery agent disposed

Error message

service discovery agent disposed

What it means

Thrown at the very entry of EnsureReadyForRequestAsync (line 412): the ServiceDiscoveryAgent entity was disposed before the request could be validated. The ET framework tracks entities through EntityRef<T>, a weak handle that returns null once the entity's Scene/fiber is torn down. Because Dispose already ran on the agent, IsReady() cannot even be evaluated, so the call aborts immediately rather than dereferencing a dead object.

Source

Thrown at Packages/cn.etetet.servicediscovery/Scripts/Hotfix/Server/ServiceDiscoveryAgentSystem.cs:412

                return false;
            }

            if (self.CurrentMasterEpoch <= 0)
            {
                self.CurrentMasterEpoch = 1;
            }

            self.SwitchToEndpoint(bootstrap.MasterActorId, "inheritable-endpoint");
            return true;
        }

        private static async ETTask EnsureReadyForRequestAsync(this ServiceDiscoveryAgent self, string requestName)
        {
            EntityRef<ServiceDiscoveryAgent> selfRef = self;
            self = selfRef;
            if (self == null)
            {
                throw new Exception("service discovery agent disposed");
            }

            if (self.IsReady())
            {
                return;
            }

            int totalRetryCount = MasterResolveMaxRetryCountWhenEndpointUnavailable;
            for (int retry = 0; retry < totalRetryCount; ++retry)
            {
                self = selfRef;
                if (self == null)
                {
                    throw new Exception("service discovery agent disposed");
                }

                if (!self.HasStatus(ServiceDiscoveryAgentStatus.Bootstrapping))
                {

View on GitHub (pinned to 5cab01f7a8)

Solutions

  1. Check agent.IsReady() and that the owning Scene is not disposing before issuing ForwardToDiscoveryByFailover; skip the call during shutdown.
  2. Ensure shutdown drains/cancels in-flight discovery requests (cancel the ETCancellationToken / fiber) before disposing the agent entity.
  3. If this surfaces as an unhandled exception during restart, confirm the restart sequence disposes the agent only after the main loop has stopped accepting new requests.

Example fix

// before
var resp = await agent.ForwardToDiscoveryByFailover<T>(req, nameof(req));

// after
if (agent == null || !agent.IsReady())
{
    return; // shutting down, skip
}
var resp = await agent.ForwardToDiscoveryByFailover<T>(req, nameof(req));
Defensive patterns

Strategy: validation

Validate before calling

// Validate agent liveness AND readiness before forwarding.
public static bool CanForward(ServiceDiscoveryAgent agent)
{
    return agent != null
        && agent.Root() != null
        && agent.IsReady(); // ServiceDiscoveryActorId != default && AgentRegistered
}

// usage
if (!CanForward(agent))
{
    Log.Warning("discovery agent not ready/disposed, skipping request");
    return;
}

Try / catch

// Tolerate dispose during shutdown at the call boundary.
try
{
    T resp = await agent.ForwardToDiscoveryByFailover<T>(req, name);
}
catch (Exception e) when (agent == null || IsShuttingDown)
{
    Log.Warning($"discovery call aborted (agent disposed/shutdown): {e.Message}");
}

Prevention

When it happens

Trigger: ForwardToDiscoveryByFailover / any internal call that reaches EnsureReadyForRequestAsync is invoked on an agent whose Root Scene is already shutting down or has been removed from its Fiber. This specific throw fires on the first selfRef dereference, so the agent was dead before the method body began executing.

Common situations: Server shutdown sequence where an outstanding RPC or heartbeat callback still references the agent; a scene reload/fiber recycle that disposes the agent while a queued continuation runs; manual Dispose called from failover logic while a concurrent request was already dispatched.

Related errors


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