`reenterConfiguration` can send `block_changed_ack` in the configuration protocol and crash the server
Nobody has claimed this yet.
- Dominant language
- Java
- Stars
- 12.7k
- Forks
- 3.5k
- Avg merge
- 3d 13h
- Merged PRs (30d)
- 11
Description
Stack trace
Crash-Report: https://mclo.gs/MYzXc1J
Log: https://mclo.gs/e55gsTg
Plugin and Datapack List
[21:52:31] [Server thread/INFO]: ℹ Server Plugins (1):
[21:52:31] [Server thread/INFO]: Paper Plugins:
[21:52:31] [Server thread/INFO]: - ReconfigurationReproducer
datapack list
[21:54:12 INFO]: There are 3 data pack(s) enabled: [vanilla (built-in)], [file/bukkit (world)], [paper (built-in)]
[21:54:12 INFO]: There are no more data packs available
Actions to reproduce (if known)
- Start a fresh server using the latest Paper 26.2 build.
- Install a minimal reproduction plugin containing the listener shown below.
- Join the server and break blocks.
- The plugin directly calls
Player#getConnection().reenterConfiguration()fromBlockBreakEvent. - When
PlayerConnectionReconfigureEventis fired, the plugin immediately callscompleteReconfiguration(). - Paper attempts to send
clientbound/minecraft:block_changed_ackafter the outbound connection has entered the configuration protocol. - The player is disconnected, followed by
IllegalStateException: Already retired. This exception can terminate the server. - Repeat the block break if the timing is not hit on the first attempt.
Minimal reproduction code:
package dev.slne.surf.testplugin;
import io.papermc.paper.event.connection.configuration.PlayerConnectionReconfigureEvent;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.block.BlockBreakEvent;
import org.bukkit.plugin.java.JavaPlugin;
public final class ReconfigurationReproducer extends JavaPlugin implements Listener {
@Override
public void onEnable() {
getServer().getPluginManager().registerEvents(this, this);
}
@EventHandler
public void onBlockBreak(final BlockBreakEvent event) {
event.getPlayer().getConnection().reenterConfiguration();
}
@EventHandler
public void onReconfigure(final PlayerConnectionReconfigureEvent event) {
event.getConnection().completeReconfiguration();
}
}
Paper version
[21:52:29] [Server thread/INFO]: Checking version, please wait...
[21:52:29] [ForkJoinPool.commonPool-worker-57/INFO]: This server is running Paper version 26.2-84-main@26e81c4 (2026-07-26T18:11:33Z) (Implementing API version 26.2.build.84-stable)
You are running the latest version
Previous version: 26.2-d9f1128 (MC: 26.2)
Other
Expected behavior:
Calling reenterConfiguration() should safely transition the player into the configuration phase, defer the transition until pending PLAY packets have been handled, or reject the call without corrupting the connection state.
It should not encode a PLAY packet using the CONFIGURATION protocol, disconnect the player, or crash the server.
Observed behavior:
The first relevant error is:
Error sending packet clientbound/minecraft:block_changed_ack
io.netty.handler.codec.EncoderException: Sending unknown packet 'clientbound/minecraft:block_changed_ack'
The player is then disconnected. During disconnect cleanup, Paper attempts to remove the player again even though the player was already removed while switching to the configuration phase. This produces:
java.lang.IllegalStateException: Already retired
at io.papermc.paper.threadedregions.EntityScheduler.retire(...)
at net.minecraft.world.entity.Entity.retireScheduler(...)
at net.minecraft.server.players.PlayerList.remove(...)
The minimal reproduction invokes reenterConfiguration() directly from BlockBreakEvent because this makes the timing easy to reproduce.
However, the issue does not appear to be inherently specific to BlockBreakEvent. It can also occur when reenterConfiguration() is invoked from another task or event while the player happens to be breaking or interacting with a block and a block change acknowledgement is pending at the time of the protocol transition.
Calling the method after a fixed delay reduces the likelihood, but does not eliminate the problem if the player continues interacting with blocks during that delay.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start by reproducing the issue with the provided ReconfigurationReproducer plugin on Paper 26.2, focusing on reenterConfiguration(), completeReconfiguration(), and the BlockBreakEvent timing. Trace the configuration transition and handling of block_changed_ack around PlayerConnectionReconfigureEvent. Done means the transition no longer encodes a PLAY packet as CONFIGURATION, disconnects the player, or causes the Already retired crash.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- java
- Domain
- networking
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 48/100