egametang/ET · critical · Exception

kchannel connected but kcp is zero!

Error message

kchannel connected but kcp is zero!

What it means

KChannel.Send throws when IsConnected is true but the kcp instance is null. This is an internal invariant violation: the connected state and the kcp lifecycle are out of sync, meaning the channel was disposed or never fully created while its flag says connected.

Source

Thrown at Packages/cn.etetet.core/Scripts/Core/Share/Network/KChannel.cs:408

					this.kcp.Send(memoryStream.GetBuffer().AsSpan((int)memoryStream.Position + alreadySendCount, sendCount));
					
					alreadySendCount += sendCount;
				}
			}

			this.Service.AddToUpdate(0, this.Id);
		}
		
		public void Send(MemoryBuffer memoryBuffer)
		{
			if (!this.IsConnected)
			{
				this.waitSendMessages.Enqueue(memoryBuffer);
				return;
			}
			if (this.kcp == null)
			{
				throw new Exception("kchannel connected but kcp is zero!");
			}
			
			// 检查等待发送的消息,如果超出最大等待大小,应该断开连接
			int n = (int)this.kcp.WaitSendCount;
			int maxWaitSize = 0;
			switch (this.Service.ServiceType)
			{
				case ServiceType.Inner:
					maxWaitSize = Kcp.InnerMaxWaitSize;
					break;
				case ServiceType.Outer:
					maxWaitSize = Kcp.OuterMaxWaitSize;
					break;
				default:
					throw new ArgumentOutOfRangeException();
			}
			if (n > maxWaitSize)
			{

View on GitHub (pinned to 5cab01f7a8)

Solutions

  1. Do not Send on a channel that is being torn down; gate sends on both IsConnected and kcp != null.
  2. Ensure OnError/Dispose clears IsConnected before nulling kcp.
  3. Report as a framework bug if it reproduces with a clean connect/disconnect cycle.

Example fix

// before
public void Send(MemoryBuffer mb) {
    if (!IsConnected) { waitSendMessages.Enqueue(mb); return; }
    if (kcp == null) throw new Exception(...);
// after
public void Send(MemoryBuffer mb) {
    if (!IsConnected || kcp == null) { waitSendMessages.Enqueue(mb); return; }
Defensive patterns

Strategy: type-guard

Validate before calling

null

Type guard

public bool CanSend => IsConnected && kcp != null;

Try / catch

null

Prevention

When it happens

Trigger: Calling Send after the kcp was disposed (e.g., on OnError/timeout path) but before IsConnected was cleared, or a connect handshake that set the flag without finishing kcp allocation.

Common situations: Disconnect/error race, double-dispose, or a version regression in the connect state machine.

Related errors


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