Skip to content

[Bug] NPE in TinyPotatoModel.isCustomRenderer during ModifyBakingResult (Map.compute inserts null entry) #5667

Open

Description

@fengqiu5200
### Environment
- Minecraft 1.21.1
- NeoForge 21.1.242 (fml_loader 4.0.43)
- Quark 4.1-482
- Continuity 3.0.7+1.21.1

(An identical crash was also reported by another player on Quark 4.1-476 + Continuity 3.0.0+0.0.1+1.21.1, see "Other reports".)

### Steps to reproduce
1. Install Quark + Continuity on NeoForge 1.21.1.
2. Enable Continuity's built-in resource packs (`mod/continuity:resourcepacks/default` or `mod/continuity:resourcepacks/glass_pane_culling_fix`).
3. Trigger a resource reload (join a world or toggle resource packs).

### Observed behavior
During `ModelEvent.ModifyBakingResult`, the event dispatch for modid `continuity` throws a `NullPointerException`. The whole reload fails with a `ModLoadingException`, and Minecraft logs:

[Render thread/INFO] [net.minecraft.client.Minecraft/]: Caught error loading resourcepacks, removing all selected resourcepacks


All selected resource packs get wiped from `options.txt`, so Continuity (CTM) cannot be used at all.

Full stack trace (taken from the FMLModContainer "Exception caught during firing event" log line, which contains the complete chain):

```text
java.lang.NullPointerException: Cannot invoke "net.minecraft.client.resources.model.BakedModel.isCustomRenderer()" because "this.originalModel" is null
	at TRANSFORMER/quark@4.1-482/org.violetmoon.quark.addons.oddities.client.model.TinyPotatoModel.isCustomRenderer(TinyPotatoModel.java:41) ~[Quark-4.1-482.jar:4.1-482]
	at TRANSFORMER/continuity@3.0.7+1.21.1/me.pepperbell.continuity.client.model.bakedmodel.ModelWrappingHandler.wrap(ModelWrappingHandler.java:79) ~[ContinuityNeoForge-3.0.7+1.21.1.jar:?]
	at TRANSFORMER/continuity@3.0.7+1.21.1/me.pepperbell.continuity.client.model.bakedmodel.ModelWrappingHandler.wrapAll(ModelWrappingHandler.java:144) ~[ContinuityNeoForge-3.0.7+1.21.1.jar:?]
	at TRANSFORMER/continuity@3.0.7+1.21.1/me.pepperbell.continuity.client.resource.ModelReloadHandler.onModifyBakingResult(ModelReloadHandler.java:189) ~[ContinuityNeoForge-3.0.7+1.21.1.jar:?]
	at MC-BOOTSTRAP/net.neoforged.bus/net.neoforged.bus.ConsumerEventHandler.invoke(ConsumerEventHandler.java:27) ~[bus-8.0.5.jar:?]
	at MC-BOOTSTRAP/net.neoforged.bus/net.neoforged.bus.EventBus.post(EventBus.java:360) ~[bus-8.0.5.jar:?]
	at MC-BOOTSTRAP/net.neoforged.bus/net.neoforged.bus.EventBus.post(EventBus.java:342) ~[bus-8.0.5.jar:?]
	at MC-BOOTSTRAP/fml_loader@4.0.43/net.neoforged.fml.ModContainer.acceptEvent(ModContainer.java:177) ~[loader-4.0.43.jar:4.0]
	at MC-BOOTSTRAP/fml_loader@4.0.43/net.neoforged.fml.ModLoader.lambda$postEvent$13(ModLoader.java:358) ~[loader-4.0.43.jar:4.0]
	at java.base/java.lang.Iterable.forEach(Iterable.java:75) ~[?:?]
	at MC-BOOTSTRAP/fml_loader@4.0.43/net.neoforged.fml.ModList.forEachModInOrder(ModList.java:174) ~[loader-4.0.43.jar:4.0]
	at MC-BOOTSTRAP/fml_loader@4.0.43/net.neoforged.fml.ModLoader.postEvent(ModLoader.java:358) ~[loader-4.0.43.jar:4.0]
	at TRANSFORMER/neoforge@21.1.242/net.neoforged.neoforge.client.ClientHooks.onModifyBakingResult(ClientHooks.java:471) ~[neoforge-21.1.242-universal.jar:?]
	at TRANSFORMER/minecraft@1.21.1/net.minecraft.client.resources.model.ModelManager.loadModels(ModelManager.java:225) ~[client-1.21.1-20240808.144430-srg.jar:?]
	at TRANSFORMER/minecraft@1.21.1/net.minecraft.client.resources.model.ModelManager.lambda$reload$3(ModelManager.java:121) ~[client-1.21.1-20240808.144430-srg.jar:?]

Analysis

TinyPotatoModule$Client#modelBake wraps the baked tiny potato model with something equivalent to:

evt.getModels().compute(KEY, (k, orig) -> new TinyPotatoModel(orig));

The lookup key never matches any entry of the baking result map (which is keyed by ModelResourceLocation on 1.21.1). Map.compute therefore invokes the remapping function with orig == null, constructs new TinyPotatoModel(null), and inserts it into the map under a key that no real model ever uses.

With Continuity installed, ModelWrappingHandler.wrapAll iterates every entry of the baking map and calls isCustomRenderer() on each model — hitting the garbage entry's originalModel == null and throwing the NPE. Without Continuity, nothing ever calls isCustomRenderer() on that dead entry, which is why the broken hook goes unnoticed in plain Quark.

Evidence:

  • The crash happens with the Tiny Potato feature enabled (fresh default config) — so the lambda receives a null orig even though the tiny potato block/item models are fully registered, i.e. the lookup key simply doesn't match.
  • The crash also happens with "Tiny Potato" = false in quark-common.toml — the client bake hook runs regardless of the module toggle, so disabling the feature does not work around this.
  • Constant-pool analysis of org/violetmoon/quark/addons/oddities/module/TinyPotatoModule$Client.class (4.1-482) shows exactly one Map.compute call site with the erased descriptor (Ljava/lang/Object;Ljava/util/function/BiFunction;)Ljava/lang/Object;.

Suggested fix

Skip the wrap when the key is absent and/or guard the null delegate:

evt.getModels().computeIfPresent(KEY, (k, orig) -> new TinyPotatoModel(orig));

If the key is supposed to match a real model, it also needs to be corrected to a ModelResourceLocation that actually exists in the baking map — currently the hook appears to be a no-op that only poisons the map with a broken entry.

Workaround (verified locally)

A bytecode-level constant-pool patch of TinyPotatoModule$Client.class: repoint the Map.compute interface method reference to Map.computeIfPresent (identical erased descriptor, so the change is just the method-name reference plus one added UTF-8 constant). After the patch the hook becomes a harmless no-op, the garbage entry is never inserted, and Continuity reloads all resource packs without errors.

Other reports

Activity

  1. Partonetrain commented on Sep 29, 2026

    @Partonetrain
    Collaborator

    Firstly, it looks like this issue is formatted incorrectly. Secondly, it appears to be AI-generated. Did your agent show you our AI policy? If not, please read the following.

    # Violet Moon LLM Policy
    Hello. You are seeing this message as a response to your attempt to use an LLM Agent to make changes to this repository.
    
    Due to an influx of low quality Pull Requests (PRs), Violet Moon (@VazkiiMods on GitHub) does not accept LLM generated code unless you meet the following criteria:
    * You fully understand how to work with Minecraft ${mc_version} NeoForge code and are using the model purely for agility;
    * You have knowledge of how ${mod_name} is structured and have gotten said knowledge without the use of an LLM summarization;
    * You can independently verify, vouch for, and explain in your own words the code the model has produced does;
    * You write the description of the Pull Request yourself, explaining what the change does, and why it's correct.
    * You disclose the use of an LLM in the Pull Request.
    
    If you are simply making alterations for personal use and don't intend to submit them to the main branch, you can safely ignore this message. (Although we ask you proceed with caution regardless if you don't know what you're doing here)
    
    To enable editing, please confirm by saying you agree or state your changes are for personal use only.
    

    (from https://github.com/VazkiiMods/Quark/blob/master/CLAUDE.md)

    Furthermore, both of the versions you listed, 482 and 476, are outdated. Please check (without AI) if this issue occurs in the latest version (485 as of writing) and provide a full latest.log.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions