PacketHandlerLog: Error: Packet exceeded HandlerComponents 'MaxOutgoingBits' value: 8182 vs 8176

We are occasionally encountering a critical networking issue during client travel.

The client can eventually become stuck on an infinite loading screen because the server fails to send essential packets.

PacketHandlerLog: Error: Packet exceeded HandlerComponents 'MaxOutgoingBits' value: 8182 vs 8176

Upon character possession, our server implementation iterates through a configuration list to grant approximately 120 GameplayAbilities via GiveAbility().

We believe this results in a massive burst of internal RPCs and replication data being queued in a single frame.

for (const TSubclassOf<UGameplayAbility>& abilityClass : abilities)
{
    if (UClass* pAbilityClass = abilityClass.Get())
    {
        FGameplayAbilitySpec abilitySpec(pAbilityClass);
        if (abilitySpec.Ability != nullptr)
        {
            AbilitySystemComponent->GiveAbility(abilitySpec);
        }
    }
}

Assuming our hypothesis regarding packet saturation is valid, we are hesitant to increase MaxOutgoingBits in our configuration. Based on standard networking best practices, exceeding the default limits risks MTU-related issues and IP fragmentation, which can lead to packet loss on unstable consumer connections.

Our current plan is to implement a batching mechanism to grant these 120 abilities in smaller “chunks” across several frames.

Why does the engine not automatically chunk or “throttle” these updates when the delta exceeds the maximum packet size?

Beyond manual batching/staggering of GiveAbility calls, does Epic recommend a specific pattern for high-density ability sets?

[Attachment Removed]

Steps to Reproduce[Attachment Removed]

Hi,

While I can’t speak to high-density ability sets specifically, batching/staggering large updates like this is a common solution to avoid bandwidth saturation.

That being said, the engine’s generic replication system does break large updates into multiple partial bunches, in order to ensure the max packet size isn’t exceeded (see UChannel::SendBunch). It’s possible there’s a bug here in how the engine is handling these packets, although I haven’t been able to find any other reports of packet handler components exceeding their MaxOutgoingBits limit.

To get a better idea of the problem, what packet handler components are you using in your project? Is this error being hit on a specific component each time, and if so, which one?

Thanks,

Alex

[Attachment Removed]

We currently have 2 components, in order:

  • Redpoint::EOS::Networking::FEmptyHandshakeHandlerComponent
  • OodleNetworkHandlerComponent

What i see happening is the following

UnrealEditor-PacketHandler.dll!PacketHandler::GetTotalReservedPacketBits() Line 1211	C++
UnrealEditor-RedpointEOSNetworking-Win64-DebugGame.dll!URedpointEOSNetConnection::InitHandler() Line 139	C++
UnrealEditor-Engine.dll!UNetConnection::InitBase(UNetDriver * InDriver, FSocket * InSocket, const FURL & InURL, EConnectionState InState, int InMaxPacket, int InPacketOverhead) Line 529	C++

PacketHandler::GetTotalReservedPacketBits() is computing MaxOutgoingBits for every component in reversed order

MaxPacketBits = 8192

1 - OodleNetworkHandlerComponent -> CurReservedBits = 16, CurMaxOutgoingBits = 8192

2 - Redpoint::EOS::Networking::FEmptyHandshakeHandlerComponent -> CurReservedBits = 0, CurMaxOutgoingBits = 8176

Then in

UnrealEditor-PacketHandler.dll!PacketHandler::Outgoing_Internal(unsigned char * Packet, int CountBits, FOutPacketTraits & Traits, bool bConnectionless, const TSharedPtr<FInternetAddr const ,1> & Address) Line 792 C++We iterate every component from the first to last, and i’m failing checking the first component FEmptyHandshakeHandlerComponent at the condition OutgoingPacket.GetNumBits() <= CurComponent.MaxOutgoingBits

At the time we check the first component in the loop, the outgoing packet has

Num = 8179

Max = 8192

GetNumBits() = 8179 is greater than the expected 8176

Speaking about FEmptyHandshakeHandlerComponent, the implementation is currently empty as stated by its name

FEmptyHandshakeHandlerComponent::FEmptyHandshakeHandlerComponent()
{
    bRequiresHandshake = true;
}
 
void FEmptyHandshakeHandlerComponent::NotifyHandshakeBegin()
{
    SetActive(true);
    FTSTicker::GetCoreTicker().AddTicker(
        FTickerDelegate::CreateSPLambda(
            this,
            [this](float DeltaSeconds) {
                SetState(UE::Handler::Component::State::Initialized);
                Initialized();
                return false;
            }),
        0.0f);
}
 
bool FEmptyHandshakeHandlerComponent::IsValid() const
{
    return true;
}
 
int32 FEmptyHandshakeHandlerComponent::GetReservedPacketBits() const
{
    return 0;
}
 
void FEmptyHandshakeHandlerComponent::Initialize()
{
}
 
void FEmptyHandshakeHandlerComponent::SetAsInitialized()
{
    SetState(UE::Handler::Component::State::Initialized);
    Initialized();
}

The thing can be reproduced, at least in our case, by spamming rpcs at the time the client player character becomes possessed, example:

for (int i = 0; i < 400; ++i)
{
	FString message = FString::Printf(TEXT("Message %d"), i);
	for (int j = 0; j < 200; ++j)
	{
		message += FString::Printf(TEXT("%d"), j);
	}
	ClientSendFoo(message);
}

[Attachment Removed]

Hi,

Thank you for the additional information! However, I’m still unable to reproduce this locally in my own test project, so I wanted to double check a few more things.

When determining the maximum bunch size, the NetConnection should account for the bits reserved by the packet handler components (see MAX_SINGLE_BUNCH_SIZE_BITS in UChannel::SendBunch). If you debug UNetConnection::GetMaxSingleBunchSizeBits, what value are you seeing?

Also, does the issue still occur without the Redpoint empty handshake handler enabled?

Finally, are you able to repro this issue in a basic sample project?

Thanks,

Alex

[Attachment Removed]

MAX_SINGLE_BUNCH_SIZE_BITS = 7627

I found a comment into the redpoint plugin explaining why the empty component is registered

/**

* At least 1 handler must require a handshake in order for Unreal Engine’s netcode to function properly, so

* we add this placeholder handshake handler component which sets itself as ready as soon as

* NotifyHandshakeBegin() is called.

*/

Unfortunately, i’ve tried to remove it and suddenly the basic travel to the host level stopped to work

I’m going to set up a sample project to test this.

In the meantime, is there anything else I should look into? I realize the flow might be complex, but if you have suggestions on where I could experiment with some changes, that would be very helpful

[Attachment Removed]

Hi,

Thanks for double checking, that value does seem expected.

A sample project would help in determining the cause. In the meanwhile, I can try and provide some pointers on functions you may want to debug.

To start, UChannel::SendBunch is where MAX_SINGLE_BUNCH_SIZE_BITS is checked to determine if the bunch will be merged or split.

From there, UNetConnection::SendRawBunch will handle writing the bunch’s header.

The packet is actually sent from UNetConnection::FlushNet, which will write the packet’s header and other info (UNetConnection::WritePacketHeader and UNetConnection::WriteFinalPacketInfo).

Finally, UIpConnection::LowLevelSend->UIpConnection::SendToRemote will handle forwarding the packet to the handler and its components.

It may be worth stepping through these functions to see what is being written to the bunch/packet, in case anything is using more bits than what was reserved.

Thanks,

Alex

[Attachment Removed]

Hi Alex, good news. I found the root cause.

It wasn’t an engine issue after all, but a bug in the Redpoint plugin. It turns out the plugin wasn’t recalculating MaxPacketHandlerBits after changes were made to the component list. Since MaxPacketHandlerBits stayed at 0, it was breaking all the downstream calculations. So sad that I haven’t noticed it earlier.

I’m gonna report the fix to the Redpoint team.

Thanks again for the help and patience!

[Attachment Removed]

Hi,

You’re very welcome, and I glad you were able to track down the cause! If you run into any other problems or have any further questions, please don’t hesitate to reach out.

Thanks,

Alex

[Attachment Removed]