babalae/better-genshin-impact · warning · InvalidOperationException

当前管道连接已经完成登记。

Error message

当前管道连接已经完成登记。

What it means

Thrown by HandleConnectionOpenAsync when connection.RemoteEndpoint is already non-null. RemoteEndpoint is set during a successful connection.open registration (for both WebView and ChildSession paths). A second connection.open on the same InstanceConnection means the peer is attempting to register a connection that is already registered, which would corrupt the registration state.

Source

Thrown at BetterGenshinImpact/Service/Instance/MessageHandlers/InstanceRequestHandler.cs:145

            InstanceIpcEnvelope.Response(request));
    }

    /// <summary>
    /// 校验子实例身份和启动记录后,将当前连接登记为有效子连接。
    /// v2 不再校验父实例 ID 或启动记录,而是使用根管道客户端的真实 PID 和 Session。
    /// </summary>
    private async Task<InstanceIpcEnvelope> HandleConnectionOpenAsync(
        InstanceConnection connection,
        InstanceIpcEnvelope request,
        CancellationToken cancellationToken)
    {
        if (_context.InstanceType != BetterGiInstanceType.Primary)
        {
            throw new InvalidOperationException("只有根实例可以接受客户端连接登记。");
        }
        if (connection.RemoteEndpoint is not null)
        {
            throw new InvalidOperationException("当前管道连接已经完成登记。");
        }
        if (connection.ClientProcessId is not { } processId
            || connection.ClientSessionId is not { } sessionId)
        {
            throw new InvalidOperationException("无法取得命名管道客户端的进程或 Session 信息。");
        }

        var open =
            request.Data?.ToObject<ConnectionOpenRequest>(InstanceIpcProtocol.Serializer)
            ?? throw new ArgumentException("连接登记请求缺少数据。");
        if (open.RequestedType == BetterGiInstanceType.WebView)
        {
            var endpoint = CreateEndpoint(
                BetterGiInstanceType.WebView,
                processId,
                sessionId);
            connection.RemoteEndpoint = endpoint;
            RegisteredInstanceConnection? replaced = null;

View on GitHub (pinned to a7cb36712d)

Solutions

  1. Ensure RootConnectionLoopAsync always creates a new InstanceConnection for each connection.open attempt rather than reusing an existing connection.
  2. Verify SendRequestAsync timeout handling does not cause duplicate open requests on the same connection.
  3. If the client times out waiting for an open response, it should dispose the connection and create a new one rather than retrying open.
  4. Catch this on the server and return a Failure response instead of throwing, so the client can handle it gracefully.
Defensive patterns

Strategy: validation

Validate before calling

// On the client side, always create a new connection for each open attempt
// Never reuse an existing InstanceConnection for a second connection.open
if (connection.RemoteEndpoint is not null)
{
    // This connection is already registered — create a new one
    await connection.DisposeAsync();
    connection = new InstanceConnection(newClient, this, _logger);
}

Prevention

When it happens

Trigger: A client sends ConnectionOpen twice on the same pipe connection without disconnecting in between. The first open sets connection.RemoteEndpoint; the second open hits this guard. This could happen if the client's RootConnectionLoopAsync has a bug that sends open twice, or if a previous open response was lost and the client retries on the same connection.

Common situations: A client retries connection.open on the same pipe after a timeout (SendRequestAsync times out but the request was actually received); a protocol version change that altered the open handshake flow; a race condition where the client's reconnect logic reuses an existing connection; debug/test code that calls open multiple times.

Related errors


AI-assisted analysis of babalae/better-genshin-impact@a7cb36712d (2026-08-13). Data as JSON: /api/errors/64fc88a6e9d8e76d. Report an issue: GitHub.