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
- Ensure RootConnectionLoopAsync always creates a new InstanceConnection for each connection.open attempt rather than reusing an existing connection.
- Verify SendRequestAsync timeout handling does not cause duplicate open requests on the same connection.
- If the client times out waiting for an open response, it should dispose the connection and create a new one rather than retrying open.
- 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
- Always create a fresh InstanceConnection for each connection.open attempt.
- Do not retry open on the same connection after a timeout — dispose and reconnect.
- Track connection state to prevent duplicate registrations.
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.