Documented and tested a varargs bug that BeansWrapper(2.3.21) has fixed: With overloaded varargs methods of different parameter counts, sometimes the last parameters of the compared methods was ignored, which is taking away a potential deciding factor and thus can lead to ambiguity error. Whether this happened depends on the order in which the Java reflection API has returned the methods, which is undocumented and known to change at least after some Java updates, breaking the application.
diff --git a/src/manual/book.xml b/src/manual/book.xml
index 8a698cb..a04c8b7 100644
--- a/src/manual/book.xml
+++ b/src/manual/book.xml
@@ -20518,6 +20518,18 @@
                       is considered to be more specific than
                       <literal>Integer</literal>).</para>
                     </listitem>
+
+                    <listitem>
+                      <para>The was a bug with overloaded varargs methods of
+                      different parameter counts, where sometimes the last
+                      parameters of the compared methods was ignored, which is
+                      taking away a potential deciding factor and thus can
+                      lead to ambiguity error. Whether this happened depends
+                      on the order in which the Java reflection API has
+                      returned the methods, which is undocumented and known to
+                      change at least after some Java updates, breaking the
+                      application.</para>
+                    </listitem>
                   </itemizedlist>
                 </listitem>
 
diff --git a/src/test/java/freemarker/test/templatesuite/models/OverloadedMethods2.java b/src/test/java/freemarker/test/templatesuite/models/OverloadedMethods2.java
index e00c79d..995828e 100644
--- a/src/test/java/freemarker/test/templatesuite/models/OverloadedMethods2.java
+++ b/src/test/java/freemarker/test/templatesuite/models/OverloadedMethods2.java
@@ -428,6 +428,14 @@
         return "mNullNonAmbiguous(int i = " + i + ")";
     }
     
+    public String mVarargsIgnoredTail(int i, double... ds) {
+        return "mVarargsIgnoredTail(int i = " + i + ", double... ds = [" + arrayToString(ds) + "])"; 
+    }
+    
+    public String mVarargsIgnoredTail(int... is) {
+        return "mVarargsIgnoredTail(int... is = [" + arrayToString(is) + "])"; 
+    }
+    
     public String mLowRankWins(int x, int y, Object o) {
         return "mLowRankWins(int x = " + x + ", int y = " + y + ", Object o = " + o + ")";
     }
diff --git a/src/test/resources/freemarker/test/templatesuite/templates/overloaded-methods-2-desc-ici-2.3.20.ftl b/src/test/resources/freemarker/test/templatesuite/templates/overloaded-methods-2-desc-ici-2.3.20.ftl
index 7c4c385..3200723 100644
--- a/src/test/resources/freemarker/test/templatesuite/templates/overloaded-methods-2-desc-ici-2.3.20.ftl
+++ b/src/test/resources/freemarker/test/templatesuite/templates/overloaded-methods-2-desc-ici-2.3.20.ftl
@@ -4,6 +4,10 @@
 <@assertEquals actual=obj.mIntPrimVSBoxed(123?short) expected="mIntPrimVSBoxed(int a1 = 123)" />
 <@assertEquals actual=obj.mIntPrimVSBoxed(123) expected="mIntPrimVSBoxed(int a1 = 123)" />
 <@assertEquals actual=obj.varargs4(1, 2, 3) expected='varargs4(int... xs = [1, 2, 3])' />
+
+<@assertFails message="multiple compatible overloaded">${obj.mVarargsIgnoredTail(1, 2, 3)}</@>
+<@assertFails message="multiple compatible overloaded">${obj.mVarargsIgnoredTail(1, 2, 3.5)}</@>
+
 <@assertEquals actual=obj.mLowRankWins(1, 2, 'a') expected='mLowRankWins(int x = 1, int y = 2, Object o = a)' />
 
 <@assertEquals actual=obj.mRareWrappings(obj.file, obj.adaptedNumber, obj.adaptedNumber, obj.adaptedNumber, obj.stringWrappedAsBoolean)
diff --git a/src/test/resources/freemarker/test/templatesuite/templates/overloaded-methods-2-ici-2.3.21.ftl b/src/test/resources/freemarker/test/templatesuite/templates/overloaded-methods-2-ici-2.3.21.ftl
index 6936c89..d31fa67 100644
--- a/src/test/resources/freemarker/test/templatesuite/templates/overloaded-methods-2-ici-2.3.21.ftl
+++ b/src/test/resources/freemarker/test/templatesuite/templates/overloaded-methods-2-ici-2.3.21.ftl
@@ -170,6 +170,10 @@
 <@assertEquals actual=obj.varargs7(1?int, 2?int) expected='varargs7(int... xs = [1, 2])' />
 <@assertEquals actual=obj.varargs7(1?short, 2?int) expected='varargs7(short a1 = 1, int... xs = [2])' />
 
+<#-- Tests that a pre-2.3.21 bug is fixed now: -->
+<@assertEquals actual=obj.mVarargsIgnoredTail(1, 2, 3) expected='mVarargsIgnoredTail(int... is = [1, 2, 3])' />
+<@assertEquals actual=obj.mVarargsIgnoredTail(1, 2, 3.5) expected='mVarargsIgnoredTail(int i = 1, double... ds = [2.0, 3.5])' />
+
 <@assertEquals actual=obj.mNullAmbiguous('a') expected='mNullAmbiguous(String s = a)' />
 <@assertEquals actual=obj.mNullAmbiguous(123) expected='mNullAmbiguous(int i = 123)' />
 <@assertEquals actual=obj.mNullAmbiguous(1.9) expected='mNullAmbiguous(int i = 1)' />
diff --git a/src/test/resources/freemarker/test/templatesuite/templates/overloaded-methods-2-inc-ici-2.3.20.ftl b/src/test/resources/freemarker/test/templatesuite/templates/overloaded-methods-2-inc-ici-2.3.20.ftl
index b0b4515..f91d7d8 100644
--- a/src/test/resources/freemarker/test/templatesuite/templates/overloaded-methods-2-inc-ici-2.3.20.ftl
+++ b/src/test/resources/freemarker/test/templatesuite/templates/overloaded-methods-2-inc-ici-2.3.20.ftl
@@ -4,6 +4,10 @@
 <@assertFails message="multiple compatible overload">${obj.mIntPrimVSBoxed(123?short)}</@>
 <@assertFails message="multiple compatible overload">${obj.mIntPrimVSBoxed(123)}</@>
 <@assertFails message="multiple compatible overload">${obj.varargs4(1, 2, 3)}</@>
+
+<@assertEquals actual=obj.mVarargsIgnoredTail(1, 2, 3) expected='mVarargsIgnoredTail(int... is = [1, 2, 3])' />
+<@assertEquals actual=obj.mVarargsIgnoredTail(1, 2, 3.5) expected='mVarargsIgnoredTail(int... is = [1, 2, 3])' />
+
 <@assertEquals actual=obj.mLowRankWins(1, 2, 'a') expected='mLowRankWins(Integer x = 1, Integer y = 2, String s = a)' />
 
 <@assertEquals actual=obj.mRareWrappings(obj.file, obj.adaptedNumber, obj.adaptedNumber, obj.adaptedNumber, obj.stringWrappedAsBoolean)