“TypeError: Failed due to illegal value in property: 0” when using a Map object in JavaScript (with Apps Script)

I’m working on a Google Apps Script project bound to a Spreadsheet. For a certain functionality, I display a modal dialog as follows: Modal dialog. So, a series of selectors, variable in number, that are created and loaded dynamically depending on some data. Each selector is contained within a <li> list element of a <menu> unordered list: <menu id="categories"></menu>.

Upon clicking on the ‘Ok’ button, a certain function is called; here is the HTML element:

<!-- An 'Ok' button to continue. -->
<input type="button" value="Ok" onclick="endDialog()" />

Now, this function endDialog extracts the data from the selectors, and builds a Map<number, number> object and an array, which in turn are passed as arguments to a server-side Apps Script function.

And here comes the problem: the endDialog function does everything correctly until the google.script.run.applyColouring(rowPairs, colourAvailabilities);, at which point I get the uncaught error described in the title: Error

Here are some insights:

  • The problem lies within the Map object part (rowPairs), since when only using the array argument (colourAvailabilities) in the server-side function, everything works nicely.
  • The server-side function is never called: it never shows in the Apps Script ‘Executions’ tab. Moreover, whenever I run this server-side function on the Apps Script side directly, with the exact same parameters, it works as expected.
  • The Map object is correctly built, or at least that is what I deduce when I log it into the console: Map object
  • When I modify the code to use an Object instead of a Map, it suddenly works. So I can make it work, but at this point I’m just curious and I want to know what on earth is going on. Besides, a Map – instead of a plain Object – is exactly what I need, since (among others) I am dealing with numbers.
  • I wonder if the problem is the impossibility of passing a Map as a parameter to the server-side function, but the documentation doesn’t seem to suggest that:

Legal parameters and return values are JavaScript primitives like a Number, Boolean, String, or null, as well as JavaScript objects and arrays that are composed of primitives, objects and arrays. A form element within the page is also legal as a parameter, but it must be the function’s only parameter, and it is not legal as a return value. Requests fail if you attempt to pass a Date, Function, DOM element besides a form, or other prohibited type, including prohibited types inside objects or arrays. Objects that create circular references will also fail, and undefined fields within arrays become null.

  • I’m not sure anymore, but I would swear that everything worked fine two days ago when I tested the finalised thing. I’m not sure what I could have modified since then.

Here is the whole HTML-side function endDialog:

  /**
   * Calls a server-side function to apply the changes 
   * to the categories' colours and closes the dialog.
   * 
   * @return {void} Nothing.
   */
  function endDialog() {
    // Hide the error messages.
    document.getElementById("emptyColour").style = "display:none;";
    document.getElementById("repeatedColour").style = "display:none;";
    // Get the list of categories.
    const categories = Array.from(document.getElementById("categories").children);
    // Create a map to contain the list of category-colour pairs.
    const rowPairs = new Map();
    // Create an array to contain the column of boolean values
    // corresponding to the availabilities of each colour palette.
    // Initialise all colours as being available.
    const colourAvailabilities = Array.from({length:10}).map(() => [true]);
    // A variable to see if all colours have been selected properly.
    var validSelects = true;
    // A variable to see if all colours are different from each other.
    var uniqueSelects = true;
    // Initialise an index variable to go through the categories.
    var i=0;
    // While the colours are validly selected,
    // and unique, go through the categories.
    while (validSelects && uniqueSelects && i<categories.length) {
      // Get the corresponding row number,
      let catRow = Number(categories[i].value);
      // and its chosen colour's row number.
      let colourRow = Number(document.getElementById(i+1)
                                     .value
                                     .split(',')[0]);
      // Check if the selected option is a valid colour.
      validSelects = colourRow != 0;
      // If the selected option is a valid colour,
      if (validSelects) {
        // set the corresponding colour to being unavailable.
        colourAvailabilities[colourRow-6][0] = false;
        // Add the rows pair to the map.
        // Use the colour row number as the key, so as to easily check whether the
        // number of categories and the number of key-value pairs in the map are equal.
        // Indeed, if this colour has already been used, no new key-value pair will
        // be added, instead just updating the value associated to the colour's row key.
        rowPairs.set(colourRow, catRow);
        // Check if all selected colours are different from each other.
        uniqueSelects = rowPairs.size == i+1;
      }
      // Go to the next category.
      i++;
    }
    // If the user has made a valid and unique colour selection,
    if (validSelects && uniqueSelects) {
      console.log(rowPairs, colourAvailabilities);
      // call a server-side function to apply the changes.
      google.script.run.applyColouring(rowPairs, colourAvailabilities);
      // Close the dialog.
      google.script.host.close();
    }
    // If one of the selected colours is invalid,
    else if (!validSelects) {
      // display an error message.
      document.getElementById("emptyColour").style = "display:block;";
    }
    // If some colour has been selected more than once,
    else {
      // display an error message.
      document.getElementById("repeatedColour").style = "display:block;";
    }
  }

I don’t believe my sharing of the rest of the code is necessary, but if you want/need either the server-side function code, or the whole HTML file, I can post them.

If anyone knows what the problem is, I’m all ears! Thanks in advance.