Skip to content

More verbose entity context checks - #306

Open
Dueris wants to merge 4 commits into
mainfrom
dev/teleport-state-debug
Open

More verbose entity context checks#306
Dueris wants to merge 4 commits into
mainfrom
dev/teleport-state-debug

Conversation

@Dueris

@Dueris Dueris commented Jul 29, 2026

Copy link
Copy Markdown
Member

For basically all of Folia's lifetime, one major flaw has been that it can have some errors that are completely undebuggable to the point of insanity. Some examples are shown below as to just how stupid these errors are, and how they have nearly NOTHING to do with the actual error in question. Some of these errors are so obscure, it's just insane. They also RARELY mention plugins in the stacktrace or the error message, meaning finding the causing plugin is extremely difficult. As such, Ive been working the last while on trying to find some way to debug these issues as best as possible to decipher many of the more common issues plugins inflict when improperly doing things in Folia, making debugging plugin issues significantly faster and easier for both server owners and developers and ourselves.

The main thing that was added is the introduction of "region transfer states." These states essentially hold on to several important pieces of data determining what is happening with the region transfer and most importantly, why. This is always set on teleports and portals of any kind, including sync-region teleports too. The information provided by these states is as such:

  • The origin of the transfer
  • The destination of the transfer
    • If the state type is a portal, and the portal has not yet found a destination target block, then it will just output the destination dimension
  • The transfer type(e.g. api_teleport_call, generic_teleport, etc)
  • The transfer reason
  • A list of StackTraceElement objects captured at the time of creating the transfer state, which can show what is triggering the teleport or portal, whether that be a plugin or not
  • A JavaPlugin instance of the first plugin caller found in the stack walker. This can be EXTREMELY useful for finding the plugin that causes these sorts of issues
  • State-specific data like the origin and destination and such

These states are placed and removed at the start and end of teleports and portals to ensure the full lifecycle is captured. However, the states are removed before the teleport/portal callbacks(and respective post events) are called, since those can trigger their own teleports and stuff, which would throw an ISE since a state is already placed.

The thread context checks provided by Folia/Paper have been modified to include this data now, which does make them a lot more verbose, but I think we can all agree verbosity is preferred over undebuggable...

There are also places where try/catch statements have been placed to catch common areas that misc plugin issues will cause to throw(in undebuggable ways), to try and help debug those areas too.

This also changes our world shutdown thread a bit, making it use these states rather than our 2 wonky fields we had before... This also changes how we block if a player is not yet removed from the world but is being removed from the world, making it so it just parks for 1ms in a loop until the player is removed(we shouldnt pass 1 iteration TBH), of which then we teleport the player BACK to the origin(read from the transfer state) location on the world shutdown thread.

Here are some examples of what errors looked like before these changes. We will even play a fun game! If you can guess what the issue is caused by without looking at the code to recreate the issue(included at the very bottom), I will give u a thumbsup reaction(I know super special, right)!! No cheatinggg!!!

1st stack before:

[19:23:53 ERROR]: [ca.spottedleaf.moonrise.common.util.TickThread] Thread failed main thread check: Cannot trigger criterion async, context=[thread=Folia Region Scheduler Thread #2,class=io.papermc.paper.threadedregions.TickRegionScheduler$TickThreadRunner,region={center=[8, 11],world=minecraft:overworld}], entity={root=[{type=ServerPlayer,id=47,uuid=a921845a-60a8-475c-9d0b-ea9b256b1b7c,pos=(-24,912.000,-1,931.000,1.000),mot=(0.000,0.000,0.000),aabb=AABB[-24912.30000001192, -1931.0, 0.699999988079071] -> [-24911.69999998808, -1929.2000000476837, 1.300000011920929],removed=null,has_vehicle=false,passenger_count=0], vehicle=[{null}], passengers=[]
java.lang.Throwable
	at ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread(TickThread.java:97) ~[main/:?]
	at net.minecraft.advancements.triggers.SimpleCriterionTrigger.trigger(SimpleCriterionTrigger.java:21) ~[main/:?]
	at net.minecraft.advancements.triggers.EntityHurtPlayerTrigger.trigger(EntityHurtPlayerTrigger.java:20) ~[main/:?]
	at net.minecraft.world.entity.LivingEntity.hurtServer(LivingEntity.java:1592) ~[main/:?]
	at net.minecraft.world.entity.player.Player.hurtServer(Player.java:751) ~[main/:?]
	at net.minecraft.server.level.ServerPlayer.hurtServer(ServerPlayer.java:1540) ~[main/:?]
	at net.minecraft.world.entity.Entity.hurt(Entity.java:2518) ~[main/:?]
	at net.minecraft.world.entity.LivingEntity.onBelowWorld(LivingEntity.java:2853) ~[main/:?]
	at net.minecraft.world.entity.Entity.checkBelowWorld(Entity.java:984) ~[main/:?]
	at net.minecraft.world.entity.Entity.baseTick(Entity.java:944) ~[main/:?]
	at net.minecraft.world.entity.LivingEntity.baseTick(LivingEntity.java:473) ~[main/:?]
	at net.minecraft.world.entity.Entity.tick(Entity.java:886) ~[main/:?]
	at net.minecraft.world.entity.LivingEntity.tick(LivingEntity.java:3461) ~[main/:?]
	at net.minecraft.world.entity.player.Player.tick(Player.java:282) ~[main/:?]
	at net.minecraft.server.level.ServerPlayer.doTick(ServerPlayer.java:1040) ~[main/:?]
	at net.minecraft.server.network.ServerGamePacketListenerImpl.tickPlayer(ServerGamePacketListenerImpl.java:400) ~[main/:?]
	at net.minecraft.server.network.ServerGamePacketListenerImpl.tick(ServerGamePacketListenerImpl.java:379) ~[main/:?]
	at net.minecraft.network.Connection.tick(Connection.java:641) ~[main/:?]
	at io.papermc.paper.threadedregions.RegionizedWorldData.tickConnections(RegionizedWorldData.java:549) ~[main/:?]
	at net.minecraft.server.MinecraftServer.tickChildren(MinecraftServer.java:1889) ~[main/:?]
	at net.minecraft.server.MinecraftServer.tickServer(MinecraftServer.java:1669) ~[main/:?]
	at net.minecraft.server.dedicated.DedicatedServer.tickServer(DedicatedServer.java:420) ~[main/:?]
	at io.papermc.paper.threadedregions.TickRegions$ConcreteRegionTickHandle.tickRegion(TickRegions.java:615) ~[main/:?]
	at io.papermc.paper.threadedregions.TickRegionScheduler$RegionScheduleHandle.runTick(TickRegionScheduler.java:604) ~[main/:?]
	at io.canvasmc.canvas.threadedregions.scheduler.AffinitySchedulerThreadPool$TickThreadRunner.run(AffinitySchedulerThreadPool.java:659) ~[main/:?]
	at java.base/java.lang.Thread.run(Thread.java:1474) ~[?:?]

2nd stack before(the initial issue that this was tested with didn't have this thread check, so that's why this 2nd one exists. It's the same thing just without the thread check):

[19:45:37 ERROR]: [ca.spottedleaf.moonrise.common.util.TickThread] Thread failed main thread check: Cannot add entity off-main thread, context=[thread=Folia Region Scheduler Thread #1,class=io.papermc.paper.threadedregions.TickRegionScheduler$TickThreadRunner,region={center=[-1546, -1],world=minecraft:overworld}], world=minecraft:overworld, chunk_pos=[7, 9]
java.lang.Throwable
	at ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread(TickThread.java:88) ~[main/:?]
	at ca.spottedleaf.moonrise.patches.chunk_system.level.entity.server.ServerEntityLookup.checkThread(ServerEntityLookup.java:53) ~[main/:?]
	at ca.spottedleaf.moonrise.patches.chunk_system.level.entity.EntityLookup.addEntity(EntityLookup.java:413) ~[main/:?]
	at ca.spottedleaf.moonrise.patches.chunk_system.level.entity.EntityLookup.addNewEntity(EntityLookup.java:397) ~[main/:?]
	at ca.spottedleaf.moonrise.patches.chunk_system.level.entity.EntityLookup.addNewEntity(EntityLookup.java:393) ~[main/:?]
	at net.minecraft.server.level.ServerLevel.addPlayer(ServerLevel.java:1754) ~[main/:?]
	at net.minecraft.server.level.ServerLevel.addDuringTeleport(ServerLevel.java:1732) ~[main/:?]
	at net.minecraft.server.level.ServerLevel.addDuringTeleport(ServerLevel.java:1726) ~[main/:?]
	at net.minecraft.server.level.ServerPlayer.placeSingleSync(ServerPlayer.java:1978) ~[main/:?]
	at net.minecraft.world.entity.Entity.lambda$placeInAsync$1(Entity.java:4383) ~[main/:?]
	at io.papermc.paper.threadedregions.RegionizedTaskQueue$PrioritisedQueue$ChunkBasedPriorityTask.executeInternal(RegionizedTaskQueue.java:545) ~[main/:?]
	at io.papermc.paper.threadedregions.RegionizedTaskQueue$PrioritisedQueue.executeTask(RegionizedTaskQueue.java:472) ~[main/:?]
	at io.papermc.paper.threadedregions.RegionizedTaskQueue$RegionTaskQueueData.executeTickTask(RegionizedTaskQueue.java:265) ~[main/:?]
	at io.papermc.paper.threadedregions.TickRegions$ConcreteRegionTickHandle.runRegionTasks(TickRegions.java:629) ~[main/:?]
	at io.papermc.paper.threadedregions.TickRegionScheduler$RegionScheduleHandle.runTasks(TickRegionScheduler.java:514) ~[main/:?]
	at io.canvasmc.canvas.threadedregions.scheduler.AffinitySchedulerThreadPool$TickThreadRunner.waitUntilDeadline(AffinitySchedulerThreadPool.java:714) ~[main/:?]
	at io.canvasmc.canvas.threadedregions.scheduler.AffinitySchedulerThreadPool$TickThreadRunner.run(AffinitySchedulerThreadPool.java:655) ~[main/:?]
	at java.base/java.lang.Thread.run(Thread.java:1474) ~[?:?]

Ok, now that you have def read these stacktraces, put your guesses in!! If you win you get 1 thumbsup emoji!! SUPER special amirite???!!

Yeah so this is caused by a plugin teleporting the player during the EntityDamageEvent. Yeah I bet none of you saw that one coming. The code:

    @EventHandler
    public void onDamage(final EntityDamageEvent damageEvent) {
        if (damageEvent.getCause() != EntityDamageEvent.DamageCause.VOID) {
            return;
        }

        damageEvent.getEntity().teleportAsync(new Location(
            damageEvent.getEntity().getWorld(),
            -24912, -1931, 1
        ));
    }

Ignore the location, this was just to simulate the initial issue that I was sent in DMs a long while ago(they figured out the issue after a tiny bit, so very good 🎊). Here are the stacktraces after my changes:

1st stack after:

[20:17:38 ERROR]: [ca.spottedleaf.moonrise.common.util.TickThread] Thread failed main thread check: Cannot trigger criterion async, context=[thread=Folia Region Scheduler Thread #1,class=io.papermc.paper.threadedregions.TickRegionScheduler$TickThreadRunner,region={center=[9, 9],world=minecraft:overworld}], entity=EntityContext:{self=[{type=ServerPlayer,entity_id=20,uuid=a921845a-60a8-475c-9d0b-ea9b256b1b7c,position=(-24,912.00,-1,931.00,1.00),delta=(0.00,0.00,0.00),aabb=AABB[-24912.30000001192, -1931.0, 0.699999988079071] -> [-24911.69999998808, -1929.2000000476837, 1.300000011920929],removed=CHANGED_DIMENSION,has_vehicle=false,passenger_count=0
	region_transfer_state=[reason="api-fronted teleport async call",type="api_teleport_call"],called_by=[
		at io.canvasmc.canvas.threadedregions.entities.EntityRegionTransferState$PluginTeleportAsyncState.<init>(EntityRegionTransferState.java:113)
		at org.bukkit.craftbukkit.entity.CraftEntity.teleportAsync(CraftEntity.java:1187)
		at org.bukkit.entity.Entity.teleportAsync(Entity.java:293)
		at org.bukkit.entity.Entity.teleportAsync(Entity.java:274)
		at dev-plugin-26.2.local-SNAPSHOT.jar//io.github.dueris.dev.handlers.DamageEventTest.onDamage(DamageEventTest.java:18)
		at org.bukkit.plugin.RegisteredListener.callEvent(RegisteredListener.java:71)
		at io.papermc.paper.plugin.manager.PaperEventManager.callEvent(PaperEventManager.java:57)
		at io.papermc.paper.plugin.manager.PaperPluginManagerImpl.callEvent(PaperPluginManagerImpl.java:131)
		at org.bukkit.plugin.SimplePluginManager.callEvent(SimplePluginManager.java:627)
		at org.bukkit.event.Event.callEvent(Event.java:46)
		at org.bukkit.craftbukkit.event.CraftEventFactory.callEntityDamageEvent(CraftEventFactory.java:1172)
		at org.bukkit.craftbukkit.event.CraftEventFactory.callEntityDamageEvent(CraftEventFactory.java:1167)
		at org.bukkit.craftbukkit.event.CraftEventFactory.handleEntityDamageEvent(CraftEventFactory.java:1095)
		at org.bukkit.craftbukkit.event.CraftEventFactory.handleEntityDamageEvent(CraftEventFactory.java:1060)
		at org.bukkit.craftbukkit.event.CraftEventFactory.handleLivingEntityDamageEvent(CraftEventFactory.java:1216)
		at net.minecraft.world.entity.LivingEntity.handleEntityDamage(LivingEntity.java:2525)
		at net.minecraft.world.entity.LivingEntity.hurtServer(LivingEntity.java:1534)
		at net.minecraft.world.entity.player.Player.hurtServer(Player.java:751)
		at net.minecraft.server.level.ServerPlayer.hurtServer(ServerPlayer.java:1540)
		at net.minecraft.world.entity.Entity.hurt(Entity.java:2566)
		at net.minecraft.world.entity.LivingEntity.onBelowWorld(LivingEntity.java:2853)
		at net.minecraft.world.entity.Entity.checkBelowWorld(Entity.java:1032)
		at net.minecraft.world.entity.Entity.baseTick(Entity.java:992)
		at net.minecraft.world.entity.LivingEntity.baseTick(LivingEntity.java:473)
		at net.minecraft.world.entity.Entity.tick(Entity.java:934)
		at net.minecraft.world.entity.LivingEntity.tick(LivingEntity.java:3461)
		at net.minecraft.world.entity.player.Player.tick(Player.java:282)
		at net.minecraft.server.level.ServerPlayer.doTick(ServerPlayer.java:1040)
		at net.minecraft.server.network.ServerGamePacketListenerImpl.tickPlayer(ServerGamePacketListenerImpl.java:400)
		at net.minecraft.server.network.ServerGamePacketListenerImpl.tick(ServerGamePacketListenerImpl.java:379)
		at net.minecraft.network.Connection.tick(Connection.java:641)
		at io.papermc.paper.threadedregions.RegionizedWorldData.tickConnections(RegionizedWorldData.java:565)
		at net.minecraft.server.MinecraftServer.tickChildren(MinecraftServer.java:1889)
		at net.minecraft.server.MinecraftServer.tickServer(MinecraftServer.java:1669)
		at net.minecraft.server.dedicated.DedicatedServer.tickServer(DedicatedServer.java:420)
		at io.papermc.paper.threadedregions.TickRegions$ConcreteRegionTickHandle.tickRegion(TickRegions.java:615)
		at io.papermc.paper.threadedregions.TickRegionScheduler$RegionScheduleHandle.runTick(TickRegionScheduler.java:604)
		at io.canvasmc.canvas.threadedregions.scheduler.AffinitySchedulerThreadPool$TickThreadRunner.run(AffinitySchedulerThreadPool.java:659)
		at java.base/java.lang.Thread.run(Thread.java:1474)
	],extra=[{first_plugin_caller=[dev-plugin],state=[origin=ServerLocation=[world=overworld,pos=(133.93828645635483, -128.58375228318462, 149.26825909824993),yaw=-71.10004,pitch=-13.200004,target=ServerLocation=[world=overworld,pos=(-24912.0, -1931.0, 1.0),yaw=0.0,pitch=0.0]}]]
ca.spottedleaf.moonrise.common.util.TickThread$ThreadViolationException
	at ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread(TickThread.java:185) ~[main/:?]
	at net.minecraft.advancements.triggers.SimpleCriterionTrigger.trigger(SimpleCriterionTrigger.java:21) ~[main/:?]
	at net.minecraft.advancements.triggers.EntityHurtPlayerTrigger.trigger(EntityHurtPlayerTrigger.java:20) ~[main/:?]
	at net.minecraft.world.entity.LivingEntity.hurtServer(LivingEntity.java:1592) ~[main/:?]
	at net.minecraft.world.entity.player.Player.hurtServer(Player.java:751) ~[main/:?]
	at net.minecraft.server.level.ServerPlayer.hurtServer(ServerPlayer.java:1540) ~[main/:?]
	at net.minecraft.world.entity.Entity.hurt(Entity.java:2566) ~[main/:?]
	at net.minecraft.world.entity.LivingEntity.onBelowWorld(LivingEntity.java:2853) ~[main/:?]
	at net.minecraft.world.entity.Entity.checkBelowWorld(Entity.java:1032) ~[main/:?]
	at net.minecraft.world.entity.Entity.baseTick(Entity.java:992) ~[main/:?]
	at net.minecraft.world.entity.LivingEntity.baseTick(LivingEntity.java:473) ~[main/:?]
	at net.minecraft.world.entity.Entity.tick(Entity.java:934) ~[main/:?]
	at net.minecraft.world.entity.LivingEntity.tick(LivingEntity.java:3461) ~[main/:?]
	at net.minecraft.world.entity.player.Player.tick(Player.java:282) ~[main/:?]
	at net.minecraft.server.level.ServerPlayer.doTick(ServerPlayer.java:1040) ~[main/:?]
	at net.minecraft.server.network.ServerGamePacketListenerImpl.tickPlayer(ServerGamePacketListenerImpl.java:400) ~[main/:?]
	at net.minecraft.server.network.ServerGamePacketListenerImpl.tick(ServerGamePacketListenerImpl.java:379) ~[main/:?]
	at net.minecraft.network.Connection.tick(Connection.java:641) ~[main/:?]
	at io.papermc.paper.threadedregions.RegionizedWorldData.tickConnections(RegionizedWorldData.java:565) ~[main/:?]
	at net.minecraft.server.MinecraftServer.tickChildren(MinecraftServer.java:1889) ~[main/:?]
	at net.minecraft.server.MinecraftServer.tickServer(MinecraftServer.java:1669) ~[main/:?]
	at net.minecraft.server.dedicated.DedicatedServer.tickServer(DedicatedServer.java:420) ~[main/:?]
	at io.papermc.paper.threadedregions.TickRegions$ConcreteRegionTickHandle.tickRegion(TickRegions.java:615) ~[main/:?]
	at io.papermc.paper.threadedregions.TickRegionScheduler$RegionScheduleHandle.runTick(TickRegionScheduler.java:604) ~[main/:?]
	at io.canvasmc.canvas.threadedregions.scheduler.AffinitySchedulerThreadPool$TickThreadRunner.run(AffinitySchedulerThreadPool.java:659) ~[main/:?]
	at java.base/java.lang.Thread.run(Thread.java:1474) ~[?:?]

2nd stack:

[20:19:36 ERROR]: [ca.spottedleaf.moonrise.common.util.TickThread] Thread failed main thread check: Cannot add entity off-main thread, context=[thread=Folia Region Scheduler Thread #1,class=io.papermc.paper.threadedregions.TickRegionScheduler$TickThreadRunner,region={center=[-1546, -1],world=minecraft:overworld}], world=minecraft:overworld, chunk_pos=[7, 9]
ca.spottedleaf.moonrise.common.util.TickThread$ThreadViolationException
	at ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread(TickThread.java:175) ~[main/:?]
	at ca.spottedleaf.moonrise.patches.chunk_system.level.entity.server.ServerEntityLookup.checkThread(ServerEntityLookup.java:53) ~[main/:?]
	at ca.spottedleaf.moonrise.patches.chunk_system.level.entity.EntityLookup.addEntity(EntityLookup.java:413) ~[main/:?]
	at ca.spottedleaf.moonrise.patches.chunk_system.level.entity.EntityLookup.addNewEntity(EntityLookup.java:397) ~[main/:?]
	at ca.spottedleaf.moonrise.patches.chunk_system.level.entity.EntityLookup.addNewEntity(EntityLookup.java:393) ~[main/:?]
	at net.minecraft.server.level.ServerLevel.addPlayer(ServerLevel.java:1754) ~[main/:?]
	at net.minecraft.server.level.ServerLevel.addDuringTeleport(ServerLevel.java:1732) ~[main/:?]
	at net.minecraft.server.level.ServerLevel.addDuringTeleport(ServerLevel.java:1726) ~[main/:?]
	at net.minecraft.server.level.ServerPlayer.placeSingleSync(ServerPlayer.java:1978) ~[main/:?]
	at net.minecraft.world.entity.Entity.lambda$placeInAsync$1(Entity.java:4432) ~[main/:?]
	at io.papermc.paper.threadedregions.RegionizedTaskQueue$PrioritisedQueue$ChunkBasedPriorityTask.executeInternal(RegionizedTaskQueue.java:545) ~[main/:?]
	at io.papermc.paper.threadedregions.RegionizedTaskQueue$PrioritisedQueue.executeTask(RegionizedTaskQueue.java:472) ~[main/:?]
	at io.papermc.paper.threadedregions.RegionizedTaskQueue$RegionTaskQueueData.executeTickTask(RegionizedTaskQueue.java:265) ~[main/:?]
	at io.papermc.paper.threadedregions.TickRegions$ConcreteRegionTickHandle.runRegionTasks(TickRegions.java:629) ~[main/:?]
	at io.papermc.paper.threadedregions.TickRegionScheduler$RegionScheduleHandle.runTasks(TickRegionScheduler.java:514) ~[main/:?]
	at io.canvasmc.canvas.threadedregions.scheduler.AffinitySchedulerThreadPool$TickThreadRunner.waitUntilDeadline(AffinitySchedulerThreadPool.java:714) ~[main/:?]
	at io.canvasmc.canvas.threadedregions.scheduler.AffinitySchedulerThreadPool$TickThreadRunner.run(AffinitySchedulerThreadPool.java:655) ~[main/:?]
	at java.base/java.lang.Thread.run(Thread.java:1474) ~[?:?]
[20:19:36 ERROR]: An unexpected error occurred when trying to place entity back in world
[20:19:36 ERROR]: Dumping all entity context information and error stack: EntityContext:{self=[{type=ServerPlayer,entity_id=48,uuid=a921845a-60a8-475c-9d0b-ea9b256b1b7c,position=(114.50,-129.13,152.50),delta=(0.00,0.00,0.00),aabb=AABB[114.19999998807907, -129.12509421556416, 152.19999998807907] -> [114.80000001192093, -127.32509426324788, 152.80000001192093],removed=null,has_vehicle=false,passenger_count=0
	region_transfer_state=[reason="api-fronted teleport async call",type="api_teleport_call"],called_by=[
		at io.canvasmc.canvas.threadedregions.entities.EntityRegionTransferState$PluginTeleportAsyncState.<init>(EntityRegionTransferState.java:113)
		at org.bukkit.craftbukkit.entity.CraftEntity.teleportAsync(CraftEntity.java:1187)
		at org.bukkit.entity.Entity.teleportAsync(Entity.java:293)
		at org.bukkit.entity.Entity.teleportAsync(Entity.java:274)
		at dev-plugin-26.2.local-SNAPSHOT.jar//io.github.dueris.dev.handlers.DamageEventTest.onDamage(DamageEventTest.java:18)
		at org.bukkit.plugin.RegisteredListener.callEvent(RegisteredListener.java:71)
		at io.papermc.paper.plugin.manager.PaperEventManager.callEvent(PaperEventManager.java:57)
		at io.papermc.paper.plugin.manager.PaperPluginManagerImpl.callEvent(PaperPluginManagerImpl.java:131)
		at org.bukkit.plugin.SimplePluginManager.callEvent(SimplePluginManager.java:627)
		at org.bukkit.event.Event.callEvent(Event.java:46)
		at org.bukkit.craftbukkit.event.CraftEventFactory.callEntityDamageEvent(CraftEventFactory.java:1172)
		at org.bukkit.craftbukkit.event.CraftEventFactory.callEntityDamageEvent(CraftEventFactory.java:1167)
		at org.bukkit.craftbukkit.event.CraftEventFactory.handleEntityDamageEvent(CraftEventFactory.java:1095)
		at org.bukkit.craftbukkit.event.CraftEventFactory.handleEntityDamageEvent(CraftEventFactory.java:1060)
		at org.bukkit.craftbukkit.event.CraftEventFactory.handleLivingEntityDamageEvent(CraftEventFactory.java:1216)
		at net.minecraft.world.entity.LivingEntity.handleEntityDamage(LivingEntity.java:2525)
		at net.minecraft.world.entity.LivingEntity.hurtServer(LivingEntity.java:1534)
		at net.minecraft.world.entity.player.Player.hurtServer(Player.java:751)
		at net.minecraft.server.level.ServerPlayer.hurtServer(ServerPlayer.java:1540)
		at net.minecraft.world.entity.Entity.hurt(Entity.java:2566)
		at net.minecraft.world.entity.LivingEntity.onBelowWorld(LivingEntity.java:2853)
		at net.minecraft.world.entity.Entity.checkBelowWorld(Entity.java:1032)
		at net.minecraft.world.entity.Entity.baseTick(Entity.java:992)
		at net.minecraft.world.entity.LivingEntity.baseTick(LivingEntity.java:473)
		at net.minecraft.world.entity.Entity.tick(Entity.java:934)
		at net.minecraft.world.entity.LivingEntity.tick(LivingEntity.java:3461)
		at net.minecraft.world.entity.player.Player.tick(Player.java:282)
		at net.minecraft.server.level.ServerPlayer.doTick(ServerPlayer.java:1040)
		at net.minecraft.server.network.ServerGamePacketListenerImpl.tickPlayer(ServerGamePacketListenerImpl.java:400)
		at net.minecraft.server.network.ServerGamePacketListenerImpl.tick(ServerGamePacketListenerImpl.java:379)
		at net.minecraft.network.Connection.tick(Connection.java:641)
		at io.papermc.paper.threadedregions.RegionizedWorldData.tickConnections(RegionizedWorldData.java:565)
		at net.minecraft.server.MinecraftServer.tickChildren(MinecraftServer.java:1889)
		at net.minecraft.server.MinecraftServer.tickServer(MinecraftServer.java:1669)
		at net.minecraft.server.dedicated.DedicatedServer.tickServer(DedicatedServer.java:420)
		at io.papermc.paper.threadedregions.TickRegions$ConcreteRegionTickHandle.tickRegion(TickRegions.java:615)
		at io.papermc.paper.threadedregions.TickRegionScheduler$RegionScheduleHandle.runTick(TickRegionScheduler.java:604)
		at io.canvasmc.canvas.threadedregions.scheduler.AffinitySchedulerThreadPool$TickThreadRunner.run(AffinitySchedulerThreadPool.java:659)
		at java.base/java.lang.Thread.run(Thread.java:1474)
	],extra=[{first_plugin_caller=[dev-plugin],state=[origin=ServerLocation=[world=overworld,pos=(114.5, -129.12509421556416, 152.5),yaw=9.900021,pitch=-14.400054,target=ServerLocation=[world=overworld,pos=(-24912.0, -1931.0, 1.0),yaw=0.0,pitch=0.0]}]]
java.lang.IllegalStateException: Thread failed main thread check: Cannot add entity off-main thread, context=[thread=Folia Region Scheduler Thread #1,class=io.papermc.paper.threadedregions.TickRegionScheduler$TickThreadRunner,region={center=[-1546, -1],world=minecraft:overworld}], world=minecraft:overworld, chunk_pos=[7, 9]
	at ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread(TickThread.java:176) ~[main/:?]
	at ca.spottedleaf.moonrise.patches.chunk_system.level.entity.server.ServerEntityLookup.checkThread(ServerEntityLookup.java:53) ~[main/:?]
	at ca.spottedleaf.moonrise.patches.chunk_system.level.entity.EntityLookup.addEntity(EntityLookup.java:413) ~[main/:?]
	at ca.spottedleaf.moonrise.patches.chunk_system.level.entity.EntityLookup.addNewEntity(EntityLookup.java:397) ~[main/:?]
	at ca.spottedleaf.moonrise.patches.chunk_system.level.entity.EntityLookup.addNewEntity(EntityLookup.java:393) ~[main/:?]
	at net.minecraft.server.level.ServerLevel.addPlayer(ServerLevel.java:1754) ~[main/:?]
	at net.minecraft.server.level.ServerLevel.addDuringTeleport(ServerLevel.java:1732) ~[main/:?]
	at net.minecraft.server.level.ServerLevel.addDuringTeleport(ServerLevel.java:1726) ~[main/:?]
	at net.minecraft.server.level.ServerPlayer.placeSingleSync(ServerPlayer.java:1978) ~[main/:?]
	at net.minecraft.world.entity.Entity.lambda$placeInAsync$1(Entity.java:4432) ~[main/:?]
	at io.papermc.paper.threadedregions.RegionizedTaskQueue$PrioritisedQueue$ChunkBasedPriorityTask.executeInternal(RegionizedTaskQueue.java:545) ~[main/:?]
	at io.papermc.paper.threadedregions.RegionizedTaskQueue$PrioritisedQueue.executeTask(RegionizedTaskQueue.java:472) ~[main/:?]
	at io.papermc.paper.threadedregions.RegionizedTaskQueue$RegionTaskQueueData.executeTickTask(RegionizedTaskQueue.java:265) ~[main/:?]
	at io.papermc.paper.threadedregions.TickRegions$ConcreteRegionTickHandle.runRegionTasks(TickRegions.java:629) ~[main/:?]
	at io.papermc.paper.threadedregions.TickRegionScheduler$RegionScheduleHandle.runTasks(TickRegionScheduler.java:514) ~[main/:?]
	at io.canvasmc.canvas.threadedregions.scheduler.AffinitySchedulerThreadPool$TickThreadRunner.waitUntilDeadline(AffinitySchedulerThreadPool.java:714) ~[main/:?]
	at io.canvasmc.canvas.threadedregions.scheduler.AffinitySchedulerThreadPool$TickThreadRunner.run(AffinitySchedulerThreadPool.java:655) ~[main/:?]
	at java.base/java.lang.Thread.run(Thread.java:1474) ~[?:?]

The 2nd stack unfortunetly does not include the extra debug in the crash report, given this kind of crash we need special handling for since it doesnt use an entity context check. The extra debug information is logged to console though(obviously). There is also a similar special check added in RegionizedWorldData#cleanupConnection since that is also a path that causes undiagnosable errors too. The extra debug information is provided whenever an entity thread check is thrown and in(at the time of writing) those 2 specific handlings. Most of the extra debug beyond the region transfer state is already existing in Folia under the EntityUtil class

Overall this is aimed to create a framework where we can easily debug current issues and further issues with plugins in the future, since obviously this wont cover EVERYTHING now, but this does cover a significant portion of issues we already see when encountered with plugin-induced exceptions.

  • Minor side note, this also does some cleanup of our teleport patches to Folia formatting-wise

@Dueris
Dueris requested review from a team July 29, 2026 03:50
Comment thread canvas-server/minecraft-patches/base/0004-Region-Threading.patch Outdated
Comment thread canvas-server/minecraft-patches/base/0004-Region-Threading.patch Outdated
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants