Crash: IllegalStateException: Recursive update in RapierVoxelColliderBakery.getPhysicsDataForBlock when Create rails place fake tracks
Versions
- Sable:
sable-neoforge-1.21.1-1.2.2
- Minecraft: 1.21.1
- Loader: NeoForge
- Create: 6.0.10
- Steam 'n Rails (railways): 0.2.0-beta.2+neoforge
- Also present in stack:
aeronautics and simulated (both inject mixins into SableCommonEvents.handleBlockChange)
What happens
The integrated server crashes during a tick while a Create track block entity is being initialised. Create's TrackBlockEntity.manageFakeTracksAlong calls Level.setBlock to place its connecting "fake track" blocks. That setBlock triggers Sable's block-change listener, which calls RapierPhysicsPipeline.handleBlockChange → RapierVoxelColliderBakery.getPhysicsDataForBlock. Inside that method, ConcurrentHashMap.computeIfAbsent (via net.minecraft.Util$8.apply) is called recursively for the same key, which Java explicitly forbids:
java.lang.IllegalStateException: Recursive update
at java.base/java.util.concurrent.ConcurrentHashMap.computeIfAbsent(ConcurrentHashMap.java:1742)
at TRANSFORMER/minecraft@1.21.1/net.minecraft.Util$8.apply(Util.java:795)
at TRANSFORMER/sable@1.2.2/dev.ryanhcode.sable.physics.impl.rapier.collider.RapierVoxelColliderBakery.getPhysicsDataForBlock(RapierVoxelColliderBakery.java:98)
at TRANSFORMER/sable@1.2.2/dev.ryanhcode.sable.physics.impl.rapier.RapierPhysicsPipeline.handleBlockChange(RapierPhysicsPipeline.java:513)
at TRANSFORMER/sable@1.2.2/dev.ryanhcode.sable.sublevel.system.SubLevelPhysicsSystem.handleBlockChange(SubLevelPhysicsSystem.java:451)
at TRANSFORMER/sable@1.2.2/dev.ryanhcode.sable.SableCommonEvents.handleBlockChange(SableCommonEvents.java:91)
at TRANSFORMER/minecraft@1.21.1/net.minecraft.world.level.chunk.LevelChunk.wrapOperation$iip000$sable$setBlockState(LevelChunk.java:8302)
at TRANSFORMER/minecraft@1.21.1/net.minecraft.world.level.chunk.LevelChunk.setBlockState(LevelChunk.java:249)
at TRANSFORMER/minecraft@1.21.1/net.minecraft.world.level.Level.setBlock(Level.java:236)
at TRANSFORMER/create@6.0.10/com.simibubi.create.content.trains.track.TrackBlockEntity.manageFakeTracksAlong(TrackBlockEntity.java:396)
at TRANSFORMER/create@6.0.10/com.simibubi.create.content.trains.track.TrackBlockEntity.lazyTick(TrackBlockEntity.java:89)
at TRANSFORMER/create@6.0.10/com.simibubi.create.foundation.blockEntity.SmartBlockEntity.initialize(SmartBlockEntity.java:71)
at TRANSFORMER/create@6.0.10/com.simibubi.create.content.trains.track.TrackBlockEntity.initialize(TrackBlockEntity.java:74)
at TRANSFORMER/create@6.0.10/com.simibubi.create.foundation.blockEntity.SmartBlockEntity.tick(SmartBlockEntity.java:76)
at TRANSFORMER/create@6.0.10/com.simibubi.create.content.trains.track.TrackBlockEntity.tick(TrackBlockEntity.java:81)
Looks like the collider cache (a ConcurrentHashMap accessed via Util$8 / memoize) is being entered recursively for the same block key while baking colliders for a chain of related blocks during a single block-change event.
Block entity at crash
Block{create:track}[shape=xo,turn=true,waterlogged=false]
- World pos
(10139, 65, -11154)
Reproduction
Single-player integrated server, world has Create rails. Crash occurs deterministically on tick when the chunk containing the curved track is loaded (the lazyTick of the track triggers manageFakeTracksAlong).
Possible root cause
computeIfAbsent on ConcurrentHashMap cannot be re-entered for the same key. If getPhysicsDataForBlock ends up resolving another block that itself resolves back into the same cached entry (or the same block needs nested collider data during baking), the recursive call into Util.memoize/computeIfAbsent throws.
Possible fixes:
- Pre-populate the cache outside of
computeIfAbsent (compute first, then putIfAbsent).
- Detect re-entrance and bail to a non-cached compute path.
- Defer collider baking from inside the block-change listener to a later tick step instead of synchronously while the world is mid-
setBlock.
Workaround
Downgrading to Sable 1.1.3 (testing this now). Removing Sable also avoids the crash.
Related
Same Recursive update symptom and same code path (RapierVoxelColliderBakery / Util.memoize computeIfAbsent) as #678 (Twilight Forest ForceFieldBlock) and #538 (Lootr DecoratedPot), but triggered here by Create's rail system instead. Filing separately because the trigger mod and call site are different and #678 is closed.
Attachments
Full crash report will be attached as a comment after issue creation (CLI doesn't support file attachments at create time).
Crash:
IllegalStateException: Recursive updateinRapierVoxelColliderBakery.getPhysicsDataForBlockwhen Create rails place fake tracksVersions
sable-neoforge-1.21.1-1.2.2aeronauticsandsimulated(both inject mixins intoSableCommonEvents.handleBlockChange)What happens
The integrated server crashes during a tick while a Create
trackblock entity is being initialised. Create'sTrackBlockEntity.manageFakeTracksAlongcallsLevel.setBlockto place its connecting "fake track" blocks. ThatsetBlocktriggers Sable's block-change listener, which callsRapierPhysicsPipeline.handleBlockChange→RapierVoxelColliderBakery.getPhysicsDataForBlock. Inside that method,ConcurrentHashMap.computeIfAbsent(vianet.minecraft.Util$8.apply) is called recursively for the same key, which Java explicitly forbids:Looks like the collider cache (a
ConcurrentHashMapaccessed viaUtil$8/ memoize) is being entered recursively for the same block key while baking colliders for a chain of related blocks during a single block-change event.Block entity at crash
Block{create:track}[shape=xo,turn=true,waterlogged=false](10139, 65, -11154)Reproduction
Single-player integrated server, world has Create rails. Crash occurs deterministically on tick when the chunk containing the curved track is loaded (the lazyTick of the track triggers
manageFakeTracksAlong).Possible root cause
computeIfAbsentonConcurrentHashMapcannot be re-entered for the same key. IfgetPhysicsDataForBlockends up resolving another block that itself resolves back into the same cached entry (or the same block needs nested collider data during baking), the recursive call intoUtil.memoize/computeIfAbsentthrows.Possible fixes:
computeIfAbsent(compute first, thenputIfAbsent).setBlock.Workaround
Downgrading to Sable 1.1.3 (testing this now). Removing Sable also avoids the crash.
Related
Same
Recursive updatesymptom and same code path (RapierVoxelColliderBakery/Util.memoizecomputeIfAbsent) as #678 (Twilight Forest ForceFieldBlock) and #538 (Lootr DecoratedPot), but triggered here by Create's rail system instead. Filing separately because the trigger mod and call site are different and #678 is closed.Attachments
Full crash report will be attached as a comment after issue creation (CLI doesn't support file attachments at create time).