Skip to content

[1.21.1 NeoForge] Client crash: ArrayIndexOutOfBoundsException in Batching sections via WrappingVisualizer / addCacheClearListener (thread-unsafe ArrayList) #5

Description

@qwer854645

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

  1. Join a multiplayer world with Azimuth 1.4.5 + Create + Flywheel (Colorwheel backend OK).
  2. Move through areas with Create / Azimuth-rendered block entities so chunk sections rebuild.
  3. 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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions