{"record":{"id":"97a5ed81e76e9f41","repo":"babalae/better-genshin-impact","slug":"bettergi-97a5ed","errorCode":null,"errorMessage":"只有根实例可以向 BetterGI 客户端分发激活消息。","messagePattern":"只有根实例可以向 BetterGI 客户端分发激活消息。","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"BetterGenshinImpact/Service/Instance/MessageHandlers/InstanceRequestHandler.cs","lineNumber":113,"sourceCode":"            _logger.LogWarning(exception, \"处理实例 IPC 请求失败：{Operation}\", request.Operation);\n            return InstanceIpcEnvelope.Failure(\n                request,\n                \"invalid_request\",\n                exception.GetBaseException().Message);\n        }\n    }\n\n    /// <summary>\n    /// 激活消息按 RequestId 去重，避免管道重试导致主窗口被重复激活。\n    /// </summary>\n    private InstanceIpcEnvelope HandleActivationDispatch(\n        InstanceConnection connection,\n        InstanceIpcEnvelope request)\n    {\n        if (_context.InstanceType == BetterGiInstanceType.Primary\n            || connection.RemoteEndpoint?.InstanceType != BetterGiInstanceType.Primary)\n        {\n            throw new InvalidOperationException(\"只有根实例可以向 BetterGI 客户端分发激活消息。\");\n        }\n\n        if (_activationResponses.TryGetValue(request.RequestId, out var cachedResponse))\n        {\n            return cachedResponse;\n        }\n\n        var activation =\n            request.Data?.ToObject<ActivationDispatchRequest>(InstanceIpcProtocol.Serializer)\n            ?? throw new ArgumentException(\"激活请求缺少命令行参数。\");\n        _enqueueActivation(activation.Arguments);\n        return CacheActivationResponse(\n            request.RequestId,\n            InstanceIpcEnvelope.Response(request));\n    }\n\n    /// <summary>\n    /// 校验子实例身份和启动记录后，将当前连接登记为有效子连接。","sourceCodeStart":95,"sourceCodeEnd":131,"githubUrl":"https://github.com/babalae/better-genshin-impact/blob/a7cb36712dcb409be610257d877fcea3597e9d6b/BetterGenshinImpact/Service/Instance/MessageHandlers/InstanceRequestHandler.cs#L95-L131","documentation":"Thrown by HandleActivationDispatch when the current instance is Primary (it sends dispatches, it should not receive them) OR when the requesting connection's RemoteEndpoint is not Primary (activation.dispatch must originate from the root). This enforces the directional contract: activation messages flow strictly from Primary to ChildSession.","triggerScenarios":"A non-Primary instance receives an activation.dispatch request from a connection whose remote endpoint is not Primary, or the Primary instance itself receives an activation.dispatch (which would be a loop). The check is: if (_context.InstanceType == Primary || connection.RemoteEndpoint?.InstanceType != Primary) throw. So Primary rejects inbound dispatches, and non-child instances reject dispatches from non-Primary peers.","commonSituations":"A misconfigured or malicious peer sends activation.dispatch to the wrong instance type; a protocol version mismatch causes the dispatcher to route activation.dispatch to a WebView instead of a ChildSession; a bug in the root's forwarding logic sends dispatch to the wrong target; a replayed or duplicated frame from a corrupt stream.","solutions":["Verify the root's HandleConnectionOpenAsync forwarding logic correctly targets ChildSession connections only when sending activation.dispatch.","Ensure only ChildSession-type instances register as activation dispatch targets — check the _state.BetterGiConnectionsBySession registration.","Catch this InvalidOperationException on the sending side (the root) and fall back to accepting the new connection directly rather than forwarding.","Log connection.RemoteEndpoint?.InstanceType at the throw site to identify which peer type sent the invalid dispatch."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// On the root side, before sending activation.dispatch, verify target is a ChildSession\nif (connection.RemoteEndpoint?.InstanceType != BetterGiInstanceType.ChildSession)\n{\n    _logger.LogWarning(\"跳过激活转发：目标不是 ChildSession\");\n    return;\n}","typeGuard":"// Check if a connection is authorized to receive activation dispatch\nstatic bool CanReceiveActivationDispatch(InstanceContext context, InstanceConnection conn)\n    => context.InstanceType != BetterGiInstanceType.Primary\n       && conn.RemoteEndpoint?.InstanceType == BetterGiInstanceType.Primary;","tryCatchPattern":null,"preventionTips":["Ensure activation.dispatch is only sent to registered ChildSession connections.","Verify the root's forwarding logic targets the correct instance type.","Log RemoteEndpoint.InstanceType at the dispatch point to catch routing errors."],"tags":["ipc","activation","authorization","instance-type","topology"],"backgroundTag":null,"analyzedSha":"a7cb36712dcb409be610257d877fcea3597e9d6b","analyzedAt":"2026-08-13T16:44:57.548Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}