A noise definition in the worldgen/noise registry that sets base_amplitude: 0.0 crashes the game on world load with IllegalArgumentException: Bounds cannot include NaN.
The value 0.0 is explicitly legal input: base_amplitude is declared as Codec.doubleRange(0.0, 1000000.0) with a default of 1.0, so 0.0 is the inclusive lower bound of its own accepted range. The crash needs only two fields and does not require normalize: "legacy", it occurs on the default normalization path (normalize defaults to ENABLED). This is a regression introduced in 26.3 Snapshot 6, which replaced the octave span based normalization with one derived from the octave amplitudes.
Snapshots 1 through 5 are not affected since neither the base_amplitude field nor the failing division existed in those versions.
Steps to reproduce:
Open the create new world screen and leave the world type as "normal".
Install the attached datapack
Create the world.
The game crashes immediately during world load, before the world can be entered
Root Cause:
In net.minecraft.world.level.levelgen.synth.NormalNoise, computeNormalizationFactor divides without guarding against a zero denominator:
private static double computeNormalizationFactor(final double targetAmplitude, final List<OctaveInfo> octaves) {
double inputDeviation = estimateDeviation(octaves);
double inputSumDeviation = inputDeviation * Math.sqrt(2.0);
double targetDeviation = targetAmplitude * 0.3333333333333333;
return targetDeviation / inputSumDeviation;
}With base_amplitude: 0.0 every octave amplitude is 0.0, so estimateDeviation returns 0.0 and targetAmplitude is 0.0. The division is 0.0/0.0 and normalizationFactor becomes NaN.
The octave is still kept, because buildOctaves decides that by testing the amplitude modifier rather than the amplitude, and the modifier defaults to 1.0. create() then multiplies the two together, 0.0 * NaN is NaN, and NoiseStack rejects that when it builds its range through Interval.ofExact. That is the crash in the attached log, at NoiseStack.<init>.
There is also a second crash site worth noting. The same NaN reaches a different throw site during decode rather than world load when normalize: "legacy" is used, because the legacy branch divides by the NaN before replacing it:
double normalizationFactor = computeNormalizationFactor(targetAmplitude, this.octaves);
if (parameters.normalize == Normalization.LEGACY) {
double parityNormalizationFactor = computeParityNormalizationFactor(...);
targetAmplitude *= parityNormalizationFactor / normalizationFactor;
normalizationFactor = parityNormalizationFactor;
}
this.range = Interval.ofSymmetric(targetAmplitude * 0.3333333333333333 * 6.0);
Thank you for helping us improve Minecraft! We saved your files: