[SPIGOT-7780] Set block during chunk unload: "Chunk not there when requested: Unloaded chunk" Created: 18/Jun/24  Updated: 25/Dec/24  Resolved: 26/Sep/24

Status: Resolved
Project: Spigot
Component/s: None
Affects Version/s: None
Fix Version/s: None

Type: Bug Priority: Minor
Reporter: blablubbabc Assignee: Unassigned
Resolution: Fixed Votes: 5
Labels: ChunkUnloadEvent

Issue Links:
Duplicate
is duplicated by SPIGOT-7821 ChunkUnloadEvent Still a bug Resolved
is duplicated by SPIGOT-7843 1.21: ChunkUnloadEvent returns unload... Resolved
is duplicated by SPIGOT-7818 Error when unloading a chunk Resolved
Version: This server is running CraftBukkit version dev-Spigot-146439e-3f8e416 (MC: 1.21) (Implementing API version 1.21-R0.1-SNAPSHOT)
Guidelines Read: Yes

 Description   

A plugin tries to update a block during the ChunkUnloadEvent, before the chunk gets unloaded.

In previous versions this worked fine (tested with Spigot for MC 1.20.6): The event was called before the server actually considers the chunk as unloaded, allowing plugins to still process the chunk during the event before the chunk is actually moved to the unloaded state. In 1.21 (even with the recent bug fix related to chunk unloads included: https://hub.spigotmc.org/stash/projects/SPIGOT/repos/craftbukkit/commits/f5a63f7345eaf167d2664dc04f4e2cf7458c26fc), the following error is observed:

java.lang.IllegalStateException: Chunk not there when requested: Unloaded chunk
        at net.minecraft.server.level.ChunkProviderServer.a(ChunkProviderServer.java:156) ~[spigot-1.21-R0.1-SNAPSHOT.jar:dev-Spigot-146439e-3f8e416]
        at net.minecraft.world.level.World.a(World.java:298) ~[spigot-1.21-R0.1-SNAPSHOT.jar:dev-Spigot-146439e-3f8e416]
        at net.minecraft.world.level.IWorldReader.a(SourceFile:156) ~[spigot-1.21-R0.1-SNAPSHOT.jar:dev-Spigot-146439e-3f8e416]
        at net.minecraft.world.level.World.d(World.java:292) ~[spigot-1.21-R0.1-SNAPSHOT.jar:dev-Spigot-146439e-3f8e416]
        at net.minecraft.world.level.World.a_(World.java:547) ~[spigot-1.21-R0.1-SNAPSHOT.jar:dev-Spigot-146439e-3f8e416]
        at org.bukkit.craftbukkit.v1_21_R1.block.CraftBlock.getNMS(CraftBlock.java:79) ~[spigot-1.21-R0.1-SNAPSHOT.jar:dev-Spigot-146439e-3f8e416]
        at org.bukkit.craftbukkit.v1_21_R1.block.CraftBlock.setTypeAndData(CraftBlock.java:192) ~[spigot-1.21-R0.1-SNAPSHOT.jar:dev-Spigot-146439e-3f8e416]
        at org.bukkit.craftbukkit.v1_21_R1.block.CraftBlock.setBlockData(CraftBlock.java:188) ~[spigot-1.21-R0.1-SNAPSHOT.jar:dev-Spigot-146439e-3f8e416]
        at org.bukkit.craftbukkit.v1_21_R1.block.CraftBlock.setType(CraftBlock.java:177) ~[spigot-1.21-R0.1-SNAPSHOT.jar:dev-Spigot-146439e-3f8e416]
        at com.nisovin.shopkeepers.shopobjects.block.base.BaseBlockShopObject.despawn(BaseBlockShopObject.java:186) ~[?:?]
        at com.nisovin.shopkeepers.shopkeeper.spawning.ShopkeeperSpawner.doDespawnShopkeeper(ShopkeeperSpawner.java:442) ~[?:?]
        at com.nisovin.shopkeepers.shopkeeper.spawning.ShopkeeperSpawner.despawnShopkeeper(ShopkeeperSpawner.java:330) ~[?:?]
        at com.nisovin.shopkeepers.shopkeeper.spawning.ShopkeeperSpawner.despawnChunkShopkeepers(ShopkeeperSpawner.java:651) ~[?:?]
        at com.nisovin.shopkeepers.shopkeeper.activation.ShopkeeperChunkActivator.deactivateChunk(ShopkeeperChunkActivator.java:576) ~[?:?]
        at com.nisovin.shopkeepers.shopkeeper.activation.ShopkeeperChunkActivator.onChunkUnload(ShopkeeperChunkActivator.java:517) ~[?:?]
        at com.nisovin.shopkeepers.shopkeeper.activation.ChunkActivationListener.onChunkUnload(ChunkActivationListener.java:58) ~[?:?]
        at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) ~[?:?]
        at java.base/java.lang.reflect.Method.invoke(Method.java:580) ~[?:?]
        at org.bukkit.plugin.java.JavaPluginLoader$1.execute(JavaPluginLoader.java:306) ~[spigot-api-1.21-R0.1-SNAPSHOT.jar:?]
        at org.bukkit.plugin.RegisteredListener.callEvent(RegisteredListener.java:70) ~[spigot-api-1.21-R0.1-SNAPSHOT.jar:?]
        at org.bukkit.plugin.SimplePluginManager.fireEvent(SimplePluginManager.java:601) ~[spigot-api-1.21-R0.1-SNAPSHOT.jar:?]
        at org.bukkit.plugin.SimplePluginManager.callEvent(SimplePluginManager.java:588) ~[spigot-api-1.21-R0.1-SNAPSHOT.jar:?]
        at net.minecraft.world.level.chunk.Chunk.unloadCallback(Chunk.java:590) ~[spigot-1.21-R0.1-SNAPSHOT.jar:dev-Spigot-146439e-3f8e416]
        at net.minecraft.server.level.PlayerChunk.lambda$updateFutures$7(PlayerChunk.java:324) ~[spigot-1.21-R0.1-SNAPSHOT.jar:dev-Spigot-146439e-3f8e416]
        at net.minecraft.server.level.PlayerChunkMap$CallbackExecutor.run(PlayerChunkMap.java:167) ~[spigot-1.21-R0.1-SNAPSHOT.jar:dev-Spigot-146439e-3f8e416]
        at net.minecraft.server.level.PlayerChunk.a(PlayerChunk.java:334) ~[spigot-1.21-R0.1-SNAPSHOT.jar:dev-Spigot-146439e-3f8e416]
        at net.minecraft.server.level.ChunkMapDistance.lambda$runAllUpdates$1(ChunkMapDistance.java:128) ~[spigot-1.21-R0.1-SNAPSHOT.jar:dev-Spigot-146439e-3f8e416]
        at java.base/java.lang.Iterable.forEach(Iterable.java:75) ~[?:?]
        at net.minecraft.server.level.ChunkMapDistance.a(ChunkMapDistance.java:127) ~[spigot-1.21-R0.1-SNAPSHOT.jar:dev-Spigot-146439e-3f8e416]
        at net.minecraft.server.level.ChunkProviderServer.s(ChunkProviderServer.java:290) ~[spigot-1.21-R0.1-SNAPSHOT.jar:dev-Spigot-146439e-3f8e416]
        at net.minecraft.server.level.ChunkProviderServer$b.B(ChunkProviderServer.java:614) ~[spigot-1.21-R0.1-SNAPSHOT.jar:dev-Spigot-146439e-3f8e416]
        at net.minecraft.server.level.ChunkProviderServer.d(ChunkProviderServer.java:286) ~[spigot-1.21-R0.1-SNAPSHOT.jar:dev-Spigot-146439e-3f8e416]
        at net.minecraft.server.MinecraftServer.bv(MinecraftServer.java:1258) ~[spigot-1.21-R0.1-SNAPSHOT.jar:dev-Spigot-146439e-3f8e416]
        at net.minecraft.server.MinecraftServer.B(MinecraftServer.java:1242) ~[spigot-1.21-R0.1-SNAPSHOT.jar:dev-Spigot-146439e-3f8e416]
        at net.minecraft.util.thread.IAsyncTaskHandler.b(SourceFile:145) ~[spigot-1.21-R0.1-SNAPSHOT.jar:dev-Spigot-146439e-3f8e416]
        at net.minecraft.server.MinecraftServer.b(MinecraftServer.java:1207) ~[spigot-1.21-R0.1-SNAPSHOT.jar:dev-Spigot-146439e-3f8e416]
        at net.minecraft.server.MinecraftServer.v_(MinecraftServer.java:1214) ~[spigot-1.21-R0.1-SNAPSHOT.jar:dev-Spigot-146439e-3f8e416]
        at net.minecraft.server.MinecraftServer.y(MinecraftServer.java:1078) ~[spigot-1.21-R0.1-SNAPSHOT.jar:dev-Spigot-146439e-3f8e416]
        at net.minecraft.server.MinecraftServer.lambda$spin$0(MinecraftServer.java:318) ~[spigot-1.21-R0.1-SNAPSHOT.jar:dev-Spigot-146439e-3f8e416]
        at java.base/java.lang.Thread.run(Thread.java:1583) [?:?]

Edit: Maybe caused by: Seems like there is a new check in GenerationChunkHolder.scheduleChunkGenerationTask -> isStatusDisallowed which is already reduced in ChunkMapDistance.runAllUpdates based on the new ticket level (updateHighestAllowedStatus), before the unload callback is handled (updateFutures).



 Comments   
Comment by Irmo van den Berge [ 25/Sep/24 ]

I made a very simple plugin to test this issue. https://ci.mg-dev.eu/view/all/job/ChunkUnloadTest/2/

Comment by Irmo van den Berge [ 25/Sep/24 ]

blablubbabc I'll have a look tomorrow. I know the chunk system quite well, but to be honest, I haven't delved that deeply into it since like version 1.14. But I can at least test your PR's changes to see if they solve the issue (and don't result in something like the unload event firing twice)

Comment by blablubbabc [ 24/Sep/24 ]

bergerkiller I have opened a PR for this with a potential solution that appears to work in my basic testing. Since you seem to have some knowledge about the inner workings of Minecraft chunk loading, please consider taking a look and let me know if you can see any issues with it.

Comment by Tau [ 18/Sep/24 ]

Indeed, this is a major issue.

For plugins that rely on chunkunloadevent to save changes, this could cause dupes.
Imagine if this happened in vanilla, and your chests didn't save when the chunk unloads. It would immediately be considered a major issue.

Comment by Irmo van den Berge [ 18/Sep/24 ]

My current workaround is to keep track of a cache of Chunk handles mapped to Bukkit chunk myself, and keep them around for the duration of chunk unload event. Then I replaced all bukkit's apis like getBlockStates() with my own that uses that NMS handle from cache. A tick delayed these handles are pruned from the cache automatically.

The handle still contains the tile entities, so this works.

Comment by Irmo van den Berge [ 18/Sep/24 ]

It's been a very long time now and this is still not fixed. I'm noticing priority is minor, is this why? This breaks my code for checking what signs exist inside a chunk at time of unloading, and is very important. This isn't just limited to setting blocks on unload.

I'm kind of puzzled by this issue existing for months.

Generated at Tue Apr 15 09:41:37 UTC 2025 using Jira 10.3.3#10030003-sha1:d220e3fefc8dfc6d47f522d3b9a20c1455e12b7b.