[SalesForce] System.UnexpectedException: Illegal arguments during execution of lightning server side action

I have the simple app which multiplies Decimal number a on Integer number b.
The simple app has the following code:

<aura:application controller="ReproduceIssueController" >
    <aura:attribute name="aa" type="Decimal" default="0.15" description="Decimal number a"/>
    <aura:attribute name="bb" type="Integer" default="10" description="Integer number b"/>
    <aura:attribute name="cc" type="Decimal"  description="Decimal number a"/>

    <lightning:input type="number" step="0.01" label="Decimal Number A"  value="{!v.aa}"/>
    <lightning:input type="number" step="1" label="Integer number B" value="{!v.bb}"/>

    <lightning:button variant="brand" label="Multiply" onclick="{! c.m }" />              
    <br/>                  
    Multiplication result  <ui:outputText value="{!v.cc}" />
</aura:application>

The app has the following Javascript controller

({
    m : function(component, event, helper) {
        var action = component.get("c.mu");
        action.setParams( {
            a : component.get("v.aa"),
            b : component.get("v.bb")
        });

        action.setCallback(this, function(response) {
            var state = response.getState();
            if (state === "SUCCESS") {
                component.set('v.cc', response.getReturnValue() );
                alert("From server: " + response.getReturnValue());
            }
            else if (state === "INCOMPLETE") {
                // do something
            }
            else if (state === "ERROR") {
                var errors = response.getError();
                if (errors) {
                    if (errors[0] && errors[0].message) {
                        console.log("Error message: " + 
                                 errors[0].message);
                    }
                } else {
                    console.log("Unknown error");
                }
            }
        });
        $A.enqueueAction(action);
    }   
})

and the following Apex Server-side controller

public class ReproduceIssueController {
    @auraEnabled
    public static Decimal mu(Decimal a, Integer b){
        return a * b;
    }
}

When I try to execute it I see the following error messages in Javascript
enter image description here

Error message: An internal server error has occurred Error ID:
109932382-3746 (119852647)
and the following error in Developer Console logs
enter image description here
System.UnexpectedException: Illegal arguments

Class.ReproduceIssueController.mu: line 4, column 1

Apparently it fails on multiplication line but it is not clear why.

Cannot Lightning handle and pass numbers correctly? Do we have to pass only strings between the Lightning front end application and Apex Server-side controller?

Best Answer

Update:

As far as I can see now in Spring 2019 version, this issue can no longer be reproduced even if I set API Version to older one for both the Lightning Application and Apex Controller. The issue seems to be fixed now, so no workaround anymore needed (like workaround described below to change signature of Apex method to Decimal mu(Decimal a, Decimal b).


Old issue explanation and workaround:

It is not obvious reading the Salesforce documentation

Number An input field for entering a number. When working with numerical input, you can use attributes like max, min, and step.

<lightning:input type="number" name="number" label="Number"
value="12345"/>

To format numerical input as a percentage or currency, set formatter to percent or currency respectively.

<lightning:input type="number" name="ItemPrice"
label="Price" value="12345" formatter="currency"/>

Fields for percentage and currency input must specify a step increment of 0.01 as required by the native implementation.

<lightning:input type="number" name="decimal" label="Enter a decimal
value" step="0.001"/>
<lightning:input type="number"
name="percentVal" label="Enter a percentage value" formatter="percent"
step="0.01" />
<lightning:input type="number" name="currencyVal"
label="Enter a dollar amount" formatter="currency" step="0.01" />

that Lightning passes every number as Decimal typed value disregarding if it is actually Integer or Decimal.

This becomes clear when you implement getType method and debug type of the input variables

public static string getType(Object o) {
    if(o==null) return '';              // we can't say much about null with our current techniques
    if(o instanceof SObject)            return ((SObject)o).getSObjectType().getDescribe().getName()+''; 
    if(o instanceof Boolean)            return 'Boolean';
    if(o instanceof Id)                 return 'Id';
    if(o instanceof String)             return 'String';
    if(o instanceof Blob)               return 'Blob';
    if(o instanceof Date)               return 'Date';
    if(o instanceof Datetime)           return 'Datetime';
    if(o instanceof Time)               return 'Time';
    if(o instanceof String)             return 'String';
    if(o instanceof Integer)            return 'Integer';
    if(o instanceof Long)               return 'Long';
    if(o instanceof Decimal)            return 'Decimal';  // we can't distinguish between decimal and double
    if(o instanceof Double)             return 'Double';   // we can't distinguish between decimal and double
    if(o instanceof List<object>)       return 'List';
    return 'Object';                    // actually we can't detect maps and sets and maps
}

@auraEnabled
public static Decimal mu(Decimal a, Integer b){
    System.debug(LoggingLevel.ERROR, '@@@ a: ' + a );
    System.debug(LoggingLevel.ERROR, '@@@ b: ' + b );

    System.debug(LoggingLevel.ERROR,  getType( a ) );
    System.debug(LoggingLevel.ERROR,  getType( b ) );
    return a * b;
}

When you look at debug logs you will see that types of both input variables are Decimal, not Integer,

14:10:36:005 USER_DEBUG [27]|ERROR|Decimal

14:10:36:005 USER_DEBUG [28]|ERROR|Decimal

enter image description here despite one might expect type of second input variable to be Integer since its attribute is declared as Integer, not as Decimal.

<aura:attribute name="bb" type="Integer" default="10" description="Integer number b"/>

So, the solution for this would be change input variable type to Decimal, if you make this simple change

@auraEnabled
public static Decimal mu(Decimal a, Decimal b){
    return a * b;
}

you will see that the code executes successfully enter image description here

So you don't have always convert any value to String to pass it from Lightning to Apex, sometimes it is enough just to have declaration change.

Related Topic