[Crash] NullPointerException in Axiom mixin on LevelRenderer.update() during server disconnect #1

Closed
opened 2026-05-29 18:04:52 +00:00 by wait4mi · 1 comment

Summary

Client crashes with a NullPointerException when LocalPlayer is null while Axiom's mixin intercepts LevelRenderer.update(). This happens during server disconnection — Axiom's wrapOperation for isSpectator does not null-check the player before calling it.

Environment

Field Value
Ekaxiom version 5.4.1
Minecraft version 26.1.2 (Fabric)
Fabric Loader 0.19.2
OS Windows 11 (amd64)
Java OpenJDK 25.0.1 (Microsoft)
Session type Non-integrated multiplayer server

Steps to Reproduce

  1. Connect to a remote multiplayer server
  2. Play for an extended session (reproduced after ~2h12min)
  3. Disconnect from the server (or connection drops)
  4. Client crashes on the render thread during or just after disconnection

Crash

java.lang.NullPointerException: Cannot invoke "net.minecraft.client.player.LocalPlayer.isSpectator()" because "<parameter1>[0]" is null
    at net.minecraft.client.renderer.LevelRenderer.mixinextras$bridge$isSpectator$244(LevelRenderer.java)
    at net.minecraft.client.renderer.LevelRenderer.wrapOperation$zdj000$axiom$update_isSpectator(LevelRenderer.java:15896)
    at net.minecraft.client.renderer.LevelRenderer.update(LevelRenderer.java:477)
    at net.minecraft.client.renderer.GameRenderer.update(GameRenderer.java:394)
    at net.minecraft.client.Minecraft.renderFrame(Minecraft.java)
    at net.minecraft.client.Minecraft.runTick(Minecraft.java:1378)
    at net.minecraft.client.Minecraft.run(Minecraft.java:991)
    at java.base/java.lang.Thread.run(Thread.java:1474)

Secondary confirmation — the level section also threw a NPE when trying to read connection:

Server brand: ~~ERROR~~ NullPointerException: Cannot read field "connection" because "this.minecraft.player" is null

Root Cause

Axiom's mixin (wrapOperation$zdj000$axiom$update_isSpectator) wraps LevelRenderer.update() to check whether the local player is in spectator mode. During disconnection, minecraft.player (the LocalPlayer) is set to null before the current render frame completes. Axiom's mixin does not guard against this case.

The fix is a null-check before accessing the player in the mixin, e.g.:

// In the axiom mixin wrapping LevelRenderer.update()
if (minecraft.player == null) {
    return; // or delegate to original
}

Additional Context

  • Crash occurred on the render thread, not the game thread
  • JVM uptime at crash: 7916s (~2h12min)
  • AutoReconnect mod present — client reconnects after the crash, so no data loss observed
  • Shader pack active: photon_v1.3b.zip (profile: high) — unlikely to be related
  • Dynamic FPS mod also wraps renderFrame but its frame in the stack is above the NPE, not the cause

Loaded Mods (relevant)

  • axiom: Ekaxiom 5.4.1 + lattice 2.0.1+26.1
  • iris 1.10.9+mc26.1.1
  • sodium 0.8.11+mc26.1.2
  • dynamic_fps 3.11.6
  • fabric-api 0.149.0+26.1.2
  • moreculling 1.7.0 (MixinSquared)
  • lambdynlights 4.10.2+26.1.2
## Summary Client crashes with a `NullPointerException` when `LocalPlayer` is null while Axiom's mixin intercepts `LevelRenderer.update()`. This happens during server disconnection — Axiom's `wrapOperation` for `isSpectator` does not null-check the player before calling it. ## Environment | Field | Value | |---|---| | Ekaxiom version | 5.4.1 | | Minecraft version | 26.1.2 (Fabric) | | Fabric Loader | 0.19.2 | | OS | Windows 11 (amd64) | | Java | OpenJDK 25.0.1 (Microsoft) | | Session type | Non-integrated multiplayer server | ## Steps to Reproduce 1. Connect to a remote multiplayer server 2. Play for an extended session (reproduced after ~2h12min) 3. Disconnect from the server (or connection drops) 4. Client crashes on the render thread during or just after disconnection ## Crash ``` java.lang.NullPointerException: Cannot invoke "net.minecraft.client.player.LocalPlayer.isSpectator()" because "<parameter1>[0]" is null at net.minecraft.client.renderer.LevelRenderer.mixinextras$bridge$isSpectator$244(LevelRenderer.java) at net.minecraft.client.renderer.LevelRenderer.wrapOperation$zdj000$axiom$update_isSpectator(LevelRenderer.java:15896) at net.minecraft.client.renderer.LevelRenderer.update(LevelRenderer.java:477) at net.minecraft.client.renderer.GameRenderer.update(GameRenderer.java:394) at net.minecraft.client.Minecraft.renderFrame(Minecraft.java) at net.minecraft.client.Minecraft.runTick(Minecraft.java:1378) at net.minecraft.client.Minecraft.run(Minecraft.java:991) at java.base/java.lang.Thread.run(Thread.java:1474) ``` Secondary confirmation — the level section also threw a NPE when trying to read `connection`: ``` Server brand: ~~ERROR~~ NullPointerException: Cannot read field "connection" because "this.minecraft.player" is null ``` ## Root Cause Axiom's mixin (`wrapOperation$zdj000$axiom$update_isSpectator`) wraps `LevelRenderer.update()` to check whether the local player is in spectator mode. During disconnection, `minecraft.player` (the `LocalPlayer`) is set to `null` before the current render frame completes. Axiom's mixin does not guard against this case. The fix is a null-check before accessing the player in the mixin, e.g.: ```java // In the axiom mixin wrapping LevelRenderer.update() if (minecraft.player == null) { return; // or delegate to original } ``` ## Additional Context - Crash occurred on the **render thread**, not the game thread - JVM uptime at crash: **7916s (~2h12min)** - `AutoReconnect` mod present — client reconnects after the crash, so no data loss observed - Shader pack active: `photon_v1.3b.zip` (profile: high) — unlikely to be related - `Dynamic FPS` mod also wraps `renderFrame` but its frame in the stack is above the NPE, not the cause ## Loaded Mods (relevant) - `axiom: Ekaxiom 5.4.1` + `lattice 2.0.1+26.1` - `iris 1.10.9+mc26.1.1` - `sodium 0.8.11+mc26.1.2` - `dynamic_fps 3.11.6` - `fabric-api 0.149.0+26.1.2` - `moreculling 1.7.0` (MixinSquared) - `lambdynlights 4.10.2+26.1.2`
Author

Update — post-mortem après lecture du code source

La majorité de ce rapport est obsolète. Voici la situation réelle après lecture de AxiomPatcher.java.


Ce que j'avais tort

Mon suggestion de fix dans le rapport initial était inexacte :

// ❌ ce que j'avais suggéré — mauvaise variable, mauvais type de retour
if (minecraft.player == null) {
    return;
}

Le mixin en question a la signature :

update_isSpectator(LocalPlayer instance, Operation<Boolean> original) → boolean

La variable à tester est le paramètre instance (arg 1), pas minecraft.player. Et le type de retour est boolean, donc return false et non return.


Ce qui existe déjà dans le patcher

Le fix est déjà implémenté dans AxiomPatcher.java, méthode rewriteMixinLevelRenderer(), commit a49e2166 du 2026-05-23 :

// Bytecode injecté en tête de update_isSpectator() :
// ALOAD 1         (instance = LocalPlayer)
// IFNONNULL notNull
// ICONST_0
// IRETURN         → return false ("pas spectateur", safe default)
// notNull:
// ... logique originale (canNoClip + original.call)

Le commentaire dans le patcher décrit même le repro différemment du mien (rollback CoreProtect → chunks rechargés brutalement → player null transitoire) mais c'est la même race condition.


Cause réelle du crash

Le jar distribué au moment du crash était une version antérieure au fix (v5.4.1-unlock1 ou v5.4.1-unlock2, buildiés le 2026-05-03). La release v5.4.1-unlock3 (2026-05-23T17:00Z) contient le fix — buildée au même instant que le commit.

Le problème était un jar non mis à jour côté client.


Action requise

  • Mettre à jour vers v5.4.1-unlock3 (ou version ultérieure)
  • Vérifier que le launcher PandoraLauncher pointe sur la bonne version du jar

Fermé comme "already fixed in v5.4.1-unlock3".

## Update — post-mortem après lecture du code source **La majorité de ce rapport est obsolète.** Voici la situation réelle après lecture de `AxiomPatcher.java`. --- ### Ce que j'avais tort Mon suggestion de fix dans le rapport initial était inexacte : ```java // ❌ ce que j'avais suggéré — mauvaise variable, mauvais type de retour if (minecraft.player == null) { return; } ``` Le mixin en question a la signature : ``` update_isSpectator(LocalPlayer instance, Operation<Boolean> original) → boolean ``` La variable à tester est le paramètre `instance` (arg 1), pas `minecraft.player`. Et le type de retour est `boolean`, donc `return false` et non `return`. --- ### Ce qui existe déjà dans le patcher Le fix est **déjà implémenté** dans `AxiomPatcher.java`, méthode `rewriteMixinLevelRenderer()`, commit [`a49e2166`](https://forgejo.ekaii.fr/exo/Ekaxiom/commit/a49e2166) du **2026-05-23** : ```java // Bytecode injecté en tête de update_isSpectator() : // ALOAD 1 (instance = LocalPlayer) // IFNONNULL notNull // ICONST_0 // IRETURN → return false ("pas spectateur", safe default) // notNull: // ... logique originale (canNoClip + original.call) ``` Le commentaire dans le patcher décrit même le repro différemment du mien (rollback CoreProtect → chunks rechargés brutalement → player null transitoire) mais c'est la même race condition. --- ### Cause réelle du crash Le jar distribué au moment du crash était une version antérieure au fix (**v5.4.1-unlock1** ou **v5.4.1-unlock2**, buildiés le 2026-05-03). La release **v5.4.1-unlock3** (2026-05-23T17:00Z) contient le fix — buildée au même instant que le commit. **Le problème était un jar non mis à jour côté client.** --- ### Action requise - [x] Mettre à jour vers **v5.4.1-unlock3** (ou version ultérieure) - [x] Vérifier que le launcher PandoraLauncher pointe sur la bonne version du jar --- *Fermé comme "already fixed in v5.4.1-unlock3".*
Sign in to join this conversation.
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
exo/Ekaxiom#1
No description provided.