Summary
Client crashes during chunk section meshing (Description: Batching sections) with:
java.lang.ArrayIndexOutOfBoundsException: Index 1 out of bounds for length 0
The throw originates in Azimuth鈥檚 mixin-injected azimuth$addCacheClearListener on Create鈥檚 SmartBlockEntity, called from CachedBehaviourExtensionAccess.get() 鈫?WrappingVisualizer.skipVanillaRender, on a ForkJoin worker thread (not the main thread).
Log
https://mclo.gs/ebjwU1k
Environment
- Minecraft: 1.21.1
- Loader: NeoForge 21.1.233
- Azimuth: 1.4.5
- Create: 6.0.10
- Flywheel: 1.0.6 (backend fell back to
colorwheel:instancing)
- Also present on the same
SectionCompiler path: Sable, Iris/Sodium, Colorwheel, Create Lazytick, and other Create addons that mixin SmartBlockEntity
- Context: multiplayer; crash after ~6 minutes in-world
Stack trace (relevant part)
Description: Batching sections
java.lang.ArrayIndexOutOfBoundsException: Index 1 out of bounds for length 0
at java.base/java.util.ArrayList.add(ArrayList.java:484)
at java.base/java.util.ArrayList.add(ArrayList.java:496)
at com.simibubi.create.foundation.blockEntity.SmartBlockEntity.azimuth$addCacheClearListener(SmartBlockEntity.java:599)
at com.cake.azimuth.behaviour.CachedBehaviourExtensionAccess.get(CachedBehaviourExtensionAccess.java:35)
at com.simibubi.create.foundation.blockEntity.SmartBlockEntity.azimuth$getRenderedExtensionCache(SmartBlockEntity.java:635)
at com.cake.azimuth.behaviour.render.WrappingVisualizer.skipVanillaRender(WrappingVisualizer.java:79)
at dev.engine_room.flywheel.lib.visualization.VisualizationHelper.tryAddBlockEntity(VisualizationHelper.java:153)
at net.minecraft.client.renderer.chunk.SectionCompiler.handler$...$flywheel$tryAddBlockEntity(SectionCompiler.java:517)
at net.minecraft.client.renderer.chunk.SectionCompiler.compile(SectionCompiler.java:66)
...
at java.base/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:188)
Suspected root cause
In SmartBlockEntityMixin:
private final List azimuth$cacheClearListeners = new ArrayList<>(); // not concurrent
public void azimuth$addCacheClearListener(Runnable cacheClearListener) {
this.azimuth$cacheClearListeners.add(cacheClearListener); // unsynchronized
}
CachedBehaviourExtensionAccess.get() lazily registers a clear listener and builds a cache with no synchronization:
if (behaviourCache != null) return behaviourCache;
asbee.azimuth$addCacheClearListener(() -> behaviourCache = null);
// then build cache...
Flywheel invokes skipVanillaRender / visualization helpers during parallel SectionCompiler work on the ForkJoinPool. Multiple workers can hit the same SmartBlockEntity concurrently 鈫?race on the non-thread-safe ArrayList 鈫?corrupted size / elementData 鈫?Index 1 out of bounds for length 0 on add.
This matches a classic concurrent ArrayList corruption pattern; it is unlikely to be a simple logic bug in Create itself.
Note: the NeoForge crash splash asks to check Sable first (Sable also mixins SectionCompiler), but the faulting frame is Azimuth鈥檚 listener list, not Sable.
Steps to reproduce
- Join a multiplayer world with Azimuth 1.4.5 + Create + Flywheel (Colorwheel backend OK).
- Move through areas with Create / Azimuth-rendered block entities so chunk sections rebuild.
- After some minutes of play / meshing, client crashes with the stack above.
(We also saw many Sable 鈥渟ub-level movement packet for a non-existent sub-level鈥?errors shortly before the crash; that may increase rebuild pressure but is not the throwing frame.)
Expected
Chunk meshing / Flywheel visualization should not crash the client.
Suggested fix (for maintainers)
- Make
azimuth$cacheClearListeners thread-safe (e.g. CopyOnWriteArrayList, or synchronize all add/clear/iterate paths).
- Synchronize or otherwise guard lazy init in
CachedBehaviourExtensionAccess.get() (double-checked locking / concurrent map / per-BE lock).
- Ideally avoid registering listeners from meshing worker threads, or ensure cache init only happens on a single known thread.
Workaround
Remove/disable Azimuth (and hard dependents) 鈥?this crash path stops occurring when Azimuth is not loaded.
Summary
Client crashes during chunk section meshing (
Description: Batching sections) with:The throw originates in Azimuth鈥檚 mixin-injected
azimuth$addCacheClearListeneron Create鈥檚SmartBlockEntity, called fromCachedBehaviourExtensionAccess.get()鈫?WrappingVisualizer.skipVanillaRender, on a ForkJoin worker thread (not the main thread).Log
https://mclo.gs/ebjwU1k
Environment
colorwheel:instancing)SectionCompilerpath: Sable, Iris/Sodium, Colorwheel, Create Lazytick, and other Create addons that mixinSmartBlockEntityStack trace (relevant part)
Suspected root cause
In
SmartBlockEntityMixin:CachedBehaviourExtensionAccess.get()lazily registers a clear listener and builds a cache with no synchronization:Flywheel invokes
skipVanillaRender/ visualization helpers during parallelSectionCompilerwork on theForkJoinPool. Multiple workers can hit the sameSmartBlockEntityconcurrently 鈫?race on the non-thread-safeArrayList鈫?corruptedsize/elementData鈫?Index 1 out of bounds for length 0onadd.This matches a classic concurrent
ArrayListcorruption pattern; it is unlikely to be a simple logic bug in Create itself.Note: the NeoForge crash splash asks to check Sable first (Sable also mixins
SectionCompiler), but the faulting frame is Azimuth鈥檚 listener list, not Sable.Steps to reproduce
(We also saw many Sable 鈥渟ub-level movement packet for a non-existent sub-level鈥?errors shortly before the crash; that may increase rebuild pressure but is not the throwing frame.)
Expected
Chunk meshing / Flywheel visualization should not crash the client.
Suggested fix (for maintainers)
azimuth$cacheClearListenersthread-safe (e.g.CopyOnWriteArrayList, or synchronize all add/clear/iterate paths).CachedBehaviourExtensionAccess.get()(double-checked locking / concurrent map / per-BE lock).Workaround
Remove/disable Azimuth (and hard dependents) 鈥?this crash path stops occurring when Azimuth is not loaded.