sObject array parameter in Lightning causes Internal Error in Apex controller

Why do I get an Internal Error when trying to pass an array of sObject values to an Apex controller action method?

I’ve posted a simple gist to demonstrate this problem to GitHub:

The way to reproduce the problem is:

  1. Enable debug logging for yourself
  2. Launch (after you’ve created everything in the gist and tweaked the namespace references)
  3. Submitting a single lead works fine using the first Submit button, as expected
  4. Adding multiple leads and then submitting all of them using the bottom Submit button causes the Internal Error

Is there something simple that I’m doing wrong? All I want to do is pass a List object as a parameter to my Apex controller from my Lightning component.


The problem is deep inside of the transport/marshalling interface layer between Lightning Components and Apex and will need to be fixed by my team.

For now I have developed a generic workaround using a small amount of Apex.

public class OneLeadController {

    public static Id createLead(Lead newLead) {
        insert newLead;
        return newLead.Id;

    public static List<Id> createLeads(String newLeads) {
        List<SObject> newSObjectsList = convertJSONToListOfSObject(newLeads);

        insert newSObjectsList;

        List<Id> newIds = new List<Id>();
        for (SObject o : newSObjectsList) {

        return newIds;

    public static Lead newLead() {
        return (Lead)Lead.sObjectType.newSObject(null, true);

    private static List<SObject> convertJSONToListOfSObject(String json) {
        Object[] values = (Object[])System.JSON.deserializeUntyped(json);

        List<SObject> newSObjectsList = new List<SObject>();
        for (Object v : values) {
            Map<String, Object> m = (Map<String, Object>)v;

            Schema.SObjectType targetType = Schema.getGlobalDescribe().get((String)m.get('sobjectType'));

            SObject o = targetType.newSObject();

            Map<String, Schema.SObjectField> fields = targetType.getDescribe().fields.getMap();
            for (String fieldName : m.keySet()) {
                // Filter out any psuedo fields such as LastNameLocal
                Schema.SObjectField fi = fields.get(fieldName);
                if (fi != null) {
                    if (fi.getDescribe().isCreateable() && fi.getDescribe().isUpdateable()) {
                        o.put(fieldName, m.get(fieldName)); 


        return newSObjectsList;

and a small corresponding change to the client side controller (oneLeadController.js) calling this:

    save : function(component, event, helper) {
        var self = this;

        var createLeads = component.get("c.createLeads");
            "newLeads": $A.util.json.encode(component.get("v.leads"))

        createLeads.setCallback(self, function(a) {
            if (a.getState() === "SUCCESS") {
                console.log("returned: %o", a.getReturnValue());
            } else {


Source : Link , Question Author : Marty C. , Answer Author : Doug Chasman

Leave a Comment