0

I have been at this for hours and am beyond frustrated. I have a script in Google Apps Script that takes 8 arguments and uses them to alter a Google Sheet accordingly.

I've gotten this far, but I only get "Error: Request had insufficient authentication scopes.

const fs = require('fs');
const readline = require('readline');
const {google} = require('googleapis');

// If modifying these scopes, delete token.json.
const SCOPES = ['https://www.googleapis.com/auth/script.projects'];
const TOKEN_PATH = 'token.json';

// Load client secrets from a local file.
fs.readFile('credentials.json', (err, content) => {
  if (err) return console.log('Error loading client secret file:', err);
  // Authorize a client with credentials, then call the Google Apps Script API.
  authorize(JSON.parse(content), callAppsScript);
});

/**
 * Create an OAuth2 client with the given credentials, and then execute the
 * given callback function.
 * @param {Object} credentials The authorization client credentials.
 * @param {function} callback The callback to call with the authorized client.
 */
function authorize(credentials, callback) {
  const {client_secret, client_id, redirect_uris} = credentials.installed;
  const oAuth2Client = new google.auth.OAuth2(
      client_id, client_secret, redirect_uris[0]);

  // Check if we have previously stored a token.
  fs.readFile(TOKEN_PATH, (err, token) => {
    if (err) return getAccessToken(oAuth2Client, callback);
    oAuth2Client.setCredentials(JSON.parse(token));
    callback(oAuth2Client);
  });
}

/**
 * Get and store new token after prompting for user authorization, and then
 * execute the given callback with the authorized OAuth2 client.
 * @param {google.auth.OAuth2} oAuth2Client The OAuth2 client to get token for.
 * @param {getEventsCallback} callback The callback for the authorized client.
 */
function getAccessToken(oAuth2Client, callback) {
  const authUrl = oAuth2Client.generateAuthUrl({
    access_type: 'offline',
    scope: SCOPES,
  });
  console.log('Authorize this app by visiting this url:', authUrl);
  const rl = readline.createInterface({
    input: process.stdin,
    output: process.stdout,
  });
  rl.question('Enter the code from that page here: ', (code) => {
    rl.close();
    oAuth2Client.getToken(code, (err, token) => {
      if (err) return console.error('Error retrieving access token', err);
      oAuth2Client.setCredentials(token);
      // Store the token to disk for later program executions
      fs.writeFile(TOKEN_PATH, JSON.stringify(token), (err) => {
        if (err) console.error(err);
        console.log('Token stored to', TOKEN_PATH);
      });
      callback(oAuth2Client);
    });
  });
}

/**
 * Creates a new script project, upload a file, and log the script's URL.
 * @param {google.auth.OAuth2} auth An authorized OAuth2 client.
 */
/**
 * Call an Apps Script function to list the folders in the user's root
 * Drive folder.
 *
 * @param {google.auth.OAuth2} auth An authorized OAuth2 client.
 */
function callAppsScript(auth) { // eslint-disable-line no-unused-vars
    const scriptId = 'MqXrmZ5VhTSo4YuWEH6-b4UfoO49Cn6ao';
    const script = google.script('v1');

    // Make the API request. The request object is included here as 'resource'.
    script.scripts.run({
      auth: auth,
      resource: {
        function: 'automateSheet',
        parameters: [
            322,
            6549.51,
            4388,
            282.98,
            454.13,
            168,
            302
        ]
      },
      scriptId: scriptId,
    }, function(err, resp) {
      if (err) {
        // The API encountered a problem before the script started executing.
        console.log('The API returned an error: ' + err);
        return;
      }
      if (resp.error) {
        // The API executed, but the script returned an error.

        // Extract the first (and only) set of error details. The values of this
        // object are the script's 'errorMessage' and 'errorType', and an array
        // of stack trace elements.
        const error = resp.error.details[0];
        console.log('Script error message: ' + error.errorMessage);
        console.log('Script error stacktrace:');

        if (error.scriptStackTraceElements) {
          // There may not be a stacktrace if the script didn't start executing.
          for (let i = 0; i < error.scriptStackTraceElements.length; i++) {
            const trace = error.scriptStackTraceElements[i];
            console.log('\t%s: %s', trace.function, trace.lineNumber);
          }
        }
      } else {
        // The structure of the result will depend upon what the Apps Script
        // function returns. Here, the function returns an Apps Script Object
        // with String keys and values, and so the result is treated as a
        // Node.js object (folderSet).
        console.log("Success");
      }
    });
  }

How do I make this work? I followed the guides for the Node.JS setup and that worked fine. I was able to remotely create a script, so why can't I remotely run one?

Google Apps Script

function automateSheet(btcQuantity, btcCost, btcCount, bchQuantity, bchCost, bchCount, merchantCount){
  if(!btcQuantity || !btcCost || !btcCount || !bchQuantity || !bchCost || !bchCount || !merchantCount){
   //Send back "Wtf are you doing?"
  } else {
  const ss = SpreadsheetApp.getActiveSpreadsheet();
  const sheet = ss.getSheets()[1];
  const charts = ss.getSheets()[0];


  sheet.insertRowBefore(4);
  //yellow
  sheet.getRange("A4").setValue(new Date());
  sheet.getRange("C4").setValue(btcQuantity);
  sheet.getRange("D4").setValue(btcCost);
  sheet.getRange("E4").setValue(btcCount);
  sheet.getRange("G4").setValue(bchQuantity);
  sheet.getRange("H4").setValue(bchCost);
  sheet.getRange("I4").setValue(bchCount);
  sheet.getRange("K4").setValue(merchantCount);

  //BTC Stats
  var btcValue = btcQuantity * btcCost;
  var bchValue = bchQuantity * bchCost;
  var totalValue = btcValue + bchValue;
  var btcValPercent = btcValue / totalValue;
  var bchValPercent = bchValue / totalValue;
  var totalCount = btcCount + bchCount;
  var btcCountPercent = btcCount / totalCount;
  var bchCountPercent = bchCount / totalCount;

  sheet.getRange("M4").setValue(btcValue);
  sheet.getRange("N4").setValue(btcValue / btcCount);
  sheet.getRange("O4").setValue(btcValPercent);
  sheet.getRange("P4").setValue(btcCountPercent);

  //BCH Stats
  sheet.getRange("R4").setValue(bchValue);
  sheet.getRange("S4").setValue(bchValue / bchCount);
  sheet.getRange("T4").setValue(bchValPercent);
  sheet.getRange("U4").setValue(bchCountPercent);

  //Combined Stats
  var combinedCount = btcCount + bchCount;
  var combinedValue = btcValue + bchValue;
  var combinedAvg = combinedValue / combinedCount;
  sheet.getRange("W4").setValue(combinedCount);
  sheet.getRange("X4").setValue(combinedValue);
  sheet.getRange("Y4").setValue(combinedAvg);

  //BTC and BCH Stats (7DMA)
  var values = SpreadsheetApp.getActiveSheet().getDataRange().getValues();

  var btcSevenQuantity = 0;
  var btcSevenCount = 0;
  var btcSevenValue = 0;

  var bchSevenQuantity = 0;
  var bchSevenCount = 0;
  var bchSevenValue = 0;
  var merchantCount = 0;

  for(n = 3; n < 10; n++){
    btcSevenQuantity += values[n][2];
    btcSevenCount += values[n][4];
    btcSevenValue += values[n][12];

    bchSevenQuantity += values[n][6];
    bchSevenCount += values[n][8];
    bchSevenValue += values[n][17];

    merchantCount += values[n][10];
  }

  var btcSevenQuantityAvg = btcSevenQuantity / 7;
  var btcSevenCountAvg = btcSevenCount / 7;
  var btcSevenValueAvg = btcSevenValue / 7;
  var btcSevenAvgVal = btcSevenValueAvg / btcSevenCountAvg;

  var bchSevenQuantityAvg = bchSevenQuantity / 7;
  var bchSevenCountAvg = bchSevenCount / 7;
  var bchSevenValueAvg = bchSevenValue / 7;
  var bchSevenAvgVal = bchSevenValueAvg / bchSevenCountAvg;

  var combinedSevenValueTotal = btcSevenValue + bchSevenValue;
  var combinedSevenCountTotal = btcSevenCount + bchSevenCount;

  var btcSevenValPer = btcSevenValue / combinedSevenValueTotal;
  var bchSevenValPer = bchSevenValue / combinedSevenValueTotal
  var btcSevenCountPer = btcSevenCount / combinedSevenCountTotal;
  var bchSevenCountPer = bchSevenCount / combinedSevenCountTotal;

  var combinedSevenCountAvg = combinedSevenCountTotal / 7;
  var combinedSevenValueAvg = combinedSevenValueTotal / 7;
  var combinedSevenAvgVal = combinedSevenValueTotal / combinedSevenCountTotal;

  var sevenMerchantAvg = merchantCount / 7;

  sheet.getRange("AA4").setValue(btcSevenQuantityAvg);
  sheet.getRange("AB4").setValue(btcSevenCountAvg);
  sheet.getRange("AC4").setValue(btcSevenValueAvg);
  sheet.getRange("AD4").setValue(btcSevenAvgVal);
  sheet.getRange("AE4").setValue(btcSevenValPer);
  sheet.getRange("AF4").setValue(btcSevenCountPer);

  sheet.getRange("AH4").setValue(bchSevenQuantityAvg);
  sheet.getRange("AI4").setValue(bchSevenCountAvg);
  sheet.getRange("AJ4").setValue(bchSevenValueAvg);
  sheet.getRange("AK4").setValue(bchSevenAvgVal);
  sheet.getRange("AL4").setValue(bchSevenValPer);
  sheet.getRange("AM4").setValue(bchSevenCountPer);

  sheet.getRange("AO4").setValue(combinedSevenCountAvg);
  sheet.getRange("AP4").setValue(combinedSevenValueAvg);
  sheet.getRange("AQ4").setValue(combinedSevenAvgVal);
  sheet.getRange("AR4").setValue(sevenMerchantAvg);

  var sevenCount = charts.getRange("G3").getValue();
  var sevenValue = charts.getRange("G4").getValue();
  var sevenAvgVal = charts.getRange("G5").getValue();
  var sevenMerchants = charts.getRange("G6").getValue();

  if(sevenCount > combinedSevenCountAvg){
    charts.getRange("G3").setBackgroundRGB(244, 199, 195);
  } else if(sevenCount < combinedSevenCountAvg){
    charts.getRange("G3").setBackgroundRGB(183, 225, 205);
  } else {
    charts.getRange("G3").setBackground("white");
  }

    if(sevenValue > combinedSevenValueAvg){
    charts.getRange("G4").setBackgroundRGB(244, 199, 195);
  } else if(sevenCount < combinedSevenCountAvg){
    charts.getRange("G4").setBackgroundRGB(183, 225, 205);
  } else {
    charts.getRange("G4").setBackground("white");
  }

    if(sevenAvgVal > combinedSevenAvgVal){
    charts.getRange("G5").setBackgroundRGB(244, 199, 195);
  } else if(sevenAvgVal < combinedSevenAvgVal){
    charts.getRange("G5").setBackgroundRGB(183, 225, 205);
  } else {
    charts.getRange("G5").setBackground("white");
  }

    if(sevenMerchants > sevenMerchantAvg){
    charts.getRange("G6").setBackgroundRGB(244, 199, 195);
  } else if(sevenMerchants < sevenMerchantAvg){
    charts.getRange("G6").setBackgroundRGB(183, 225, 205);
  } else {
    charts.getRange("G6").setBackground("white");
  }

  charts.getRange("G3").setValue(Math.round(combinedSevenCountAvg));
  charts.getRange("G4").setValue(parseFloat(combinedSevenValueAvg).toFixed(2));
  charts.getRange("G5").setValue(parseFloat(combinedSevenAvgVal).toFixed(2));
  charts.getRange("G6").setValue(Math.round(sevenMerchantAvg));


  //BTC and BCH Stats (30DMA)

  var values = SpreadsheetApp.getActiveSheet().getDataRange().getValues();

  var btcThirtyQuantity = 0;
  var btcThirtyCount = 0;
  var btcThirtyValue = 0;

  var bchThirtyQuantity = 0;
  var bchThirtyCount = 0;
  var bchThirtyValue = 0;
  var merchantCount = 0;

  for(n = 3; n < 33; n++){
    btcThirtyQuantity += values[n][2];
    btcThirtyCount += values[n][4];
    btcThirtyValue += values[n][12];

    bchThirtyQuantity += values[n][6];
    bchThirtyCount += values[n][8];
    bchThirtyValue += values[n][17];

    merchantCount += values[n][10];
  }

  var btcThirtyQuantityAvg = btcThirtyQuantity / 30;
  var btcThirtyCountAvg = btcThirtyCount / 30;
  var btcThirtyValueAvg = btcThirtyValue / 30;
  var btcThirtyAvgVal = btcThirtyValueAvg / btcThirtyCountAvg;

  var bchThirtyQuantityAvg = bchThirtyQuantity / 30;
  var bchThirtyCountAvg = bchThirtyCount / 30;
  var bchThirtyValueAvg = bchThirtyValue / 30;
  var bchThirtyAvgVal = bchThirtyValueAvg / bchThirtyCountAvg;

  var combinedValueTotal = btcThirtyValue + bchThirtyValue;
  var combinedCountTotal = btcThirtyCount + bchThirtyCount;

  var btcThirtyValPer = btcThirtyValue / combinedValueTotal;
  var bchThirtyValPer = bchThirtyValue / combinedValueTotal
  var btcThirtyCountPer = btcThirtyCount / combinedCountTotal;
  var bchThirtyCountPer = bchThirtyCount / combinedCountTotal;

  var combinedCountAvg = combinedCountTotal / 30;
  var combinedValueAvg = combinedValueTotal / 30;
  var combinedAvgVal = combinedValueTotal / combinedCountTotal;

  var thirtyMerchantAvg = merchantCount / 30;

  sheet.getRange("AT4").setValue(btcThirtyQuantityAvg);
  sheet.getRange("AU4").setValue(btcThirtyCountAvg);
  sheet.getRange("AV4").setValue(btcThirtyValueAvg);
  sheet.getRange("AW4").setValue(btcThirtyAvgVal);
  sheet.getRange("AX4").setValue(btcThirtyValPer);
  sheet.getRange("AY4").setValue(btcThirtyCountPer);

  sheet.getRange("BA4").setValue(bchThirtyQuantityAvg);
  sheet.getRange("BB4").setValue(bchThirtyCountAvg);
  sheet.getRange("BC4").setValue(bchThirtyValueAvg);
  sheet.getRange("BD4").setValue(bchThirtyAvgVal);
  sheet.getRange("BE4").setValue(bchThirtyValPer);
  sheet.getRange("BF4").setValue(bchThirtyCountPer);

  sheet.getRange("BH4").setValue(combinedCountAvg);
  sheet.getRange("BI4").setValue(combinedValueAvg);
  sheet.getRange("BJ4").setValue(combinedAvgVal);
  sheet.getRange("BK4").setValue(thirtyMerchantAvg);

  var thirtyCount = charts.getRange("H3").getValue();
  var thirtyValue = charts.getRange("H4").getValue();
  var thirtyAvgVal = charts.getRange("H5").getValue();
  var thirtyMerchants = charts.getRange("H6").getValue();

  if(thirtyCount > combinedCountAvg){
    charts.getRange("H3").setBackgroundRGB(244, 199, 195);
  } else if(thirtyCount < combinedCountAvg){
    charts.getRange("H3").setBackgroundRGB(183, 225, 205);
  } else {
    charts.getRange("H3").setBackground("white");
  }

    if(thirtyValue > combinedValueAvg){
    charts.getRange("H4").setBackgroundRGB(244, 199, 195);
  } else if(thirtyCount < combinedCountAvg){
    charts.getRange("H4").setBackgroundRGB(183, 225, 205);
  } else {
    charts.getRange("H4").setBackground("white");
  }

    if(thirtyAvgVal > combinedAvgVal){
    charts.getRange("H5").setBackgroundRGB(244, 199, 195);
  } else if(thirtyAvgVal < combinedAvgVal){
    charts.getRange("H5").setBackgroundRGB(183, 225, 205);
  } else {
    charts.getRange("H5").setBackground("white");
  }

    if(thirtyMerchants > thirtyMerchantAvg){
    charts.getRange("H6").setBackgroundRGB(244, 199, 195);
  } else if(thirtyMerchants < thirtyMerchantAvg){
    charts.getRange("H6").setBackgroundRGB(183, 225, 205);
  } else {
    charts.getRange("H6").setBackground("white");
  }

  charts.getRange("H3").setValue(Math.round(combinedCountAvg));
  charts.getRange("H4").setValue(parseFloat(combinedValueAvg).toFixed(2));
  charts.getRange("H5").setValue(parseFloat(combinedAvgVal).toFixed(2));
  charts.getRange("H6").setValue(Math.round(thirtyMerchantAvg));




  //Charts
  var dailyVolume = charts.getCharts()[0];
  var range = charts.getRange("'Raw Data'!A4:A368");
  var range2 = charts.getRange("'Raw Data'!BI4:BI368");
  var range3 = charts.getRange("'Raw Data'!AV4:AV368");
  var range4 = charts.getRange("'Raw Data'!BC4:BC1761");
  var chart = dailyVolume.modify()
    .clearRanges()
    .addRange(range)
    .addRange(range2)
    .addRange(range3)
    .addRange(range4)
    .build();
  charts.updateChart(chart);

  var avgOrderValue = charts.getCharts()[1];
  var range = charts.getRange("'Raw Data'!A4:A368");
  var range2 = charts.getRange("'Raw Data'!BJ4:BJ368");
  var range3 = charts.getRange("'Raw Data'!AW4:AW368");
  var range4 = charts.getRange("'Raw Data'!BD4:BD1761");
  var chart = avgOrderValue.modify()
    .clearRanges()
    .addRange(range)
    .addRange(range2)
    .addRange(range3)
    .addRange(range4)
    .build();
  charts.updateChart(chart);

  var bchMarketShare = charts.getCharts()[2];
  var range = charts.getRange("'Raw Data'!A4:A115");
  var range2 = charts.getRange("'Raw Data'!AL4:AL1761");
  var range3 = charts.getRange("'Raw Data'!AM4:AM1761");
  var range4 = charts.getRange("'Raw Data'!BE4:BE1761");
  var range5 = charts.getRange("'Raw Data'!BF4:BF1761");
  var chart = bchMarketShare.modify()
    .clearRanges()
    .addRange(range)
    .addRange(range2)
    .addRange(range3)
    .addRange(range4)
    .addRange(range5)
    .build();
  charts.updateChart(chart);

  var dailyCount = charts.getCharts()[3];
  var range = charts.getRange("'Raw Data'!A4:A368");
  var range2 = charts.getRange("'Raw Data'!BH4:BH368");
  var range3 = charts.getRange("'Raw Data'!AU4:AU368");
  var range4 = charts.getRange("'Raw Data'!BB4:BB1761");
  var chart = dailyCount.modify()
    .clearRanges()
    .addRange(range)
    .addRange(range2)
    .addRange(range3)
    .addRange(range4)
    .build();
  charts.updateChart(chart);

  var dailyMerchants = charts.getCharts()[4];
  var range = charts.getRange("'Raw Data'!A4:A368");
  var range2 = charts.getRange("'Raw Data'!BK4:BK368");
  var chart = dailyMerchants.modify()
    .clearRanges()
    .addRange(range)
    .addRange(range2)
    .build();
  charts.updateChart(chart);
  }
}



automateSheet(322, 6549.51, 4388, 282.98, 454.13, 168, 302);

Updated node function

      function callScriptFunction(auth) {
        var scriptId = "M1bIaRPIiFDfQlsKPJEAKTrwdKP7CN6eO";
        var script = google.script('v1');

        script.scripts.run ({
            auth: auth,
            resource: {
                function: 'automateSheet'
            },
            scriptId: scriptId,
        }, function(err, resp){
            if(err){
                console.log(err);
            }
            else {
                var r = resp.data;
if ("error" in r) {
    console.log("Error: %o", r.error);
} else {
    console.log("Result: %o", r.response.result);
}
            }
        });
      }
Setheroni
  • 145
  • 2
  • 12
  • Running a script requires different permissions, depending on what it does. If your node code reads your Gmail, you need Gmail scope for node's OAuth credentials. If your Apps Script code reads Gmail, then... surprise, it needs Gmail scope. You can review all the scopes required for Apps Script code in the associated documentation. – tehhowch Sep 28 '18 at 04:34
  • This script only updates a google sheet. I have tried adding every relevant scope to my request and to the script's manifest file. – Setheroni Sep 28 '18 at 18:48
  • Have you run the Apps Script from the editor? – tehhowch Sep 28 '18 at 20:30
  • Yeah, it works perfectly fine. I'm trying to update my credentials now. I have the client id and client secret. Where do I go from there? Can I also just run the script with an ajax call? – Setheroni Sep 28 '18 at 20:42
  • the full list of scopes you can add is here - https://developers.google.com/identity/protocols/oauth2/scopes – Dan Alboteanu Jun 22 '23 at 14:43

1 Answers1

2

How about this confirmation and modification?

Confirmation points:

  • Please confirm scopes which are used by Google Apps Script at the script editor.
    • File -> Project properties -> Scopes
    • Copy them.
    • Those scopes are used for running Google Apps Script. If the scopes of script editor is not included in the client script (in your case, the script of node.js), the error of Request had insufficient authentication scopes. occurs.
  • Please confirm whether Apps Script API is enabled at API console, again.

Modification points:

  • Please include the scopes which copied at above to the script of node.js side.
  • About parameters of resource, in your case, the function of Google Apps Script is required to have 7 arguments like function automateSheet(a, b, c, d, e, f, g) {}. If you want to use then as an array, please modify to parameters: [[322,6549.51,4388,282.98,454.13,168,302]]. By this, you can set the function like function automateSheet(a) {}.
  • How about adding a property of devMode: true to resource? By this, when you modify the Google Apps Script, the script of node.js side can use the latest Google Apps Script. If devMode is false, when you modify Google Apps Script, it is required to update the version of script.

When above modification is reflected to your script, please modify as follows.

Modification part 1:

From:
const SCOPES = ['https://www.googleapis.com/auth/script.projects'];
To:
const SCOPES = ['https://www.googleapis.com/auth/script.projects', 'https://www.googleapis.com/auth/spreadsheets'];

I thought that from your question, at least, https://www.googleapis.com/auth/spreadsheets might be included. If other scopes are used in your Google Apps Script, also please add them.

Modification part 2:

From:
script.scripts.run({
  auth: auth,
  resource: {
    function: 'automateSheet',
    parameters: [
        322,
        6549.51,
        4388,
        282.98,
        454.13,
        168,
        302
    ]
  },
  scriptId: scriptId,
}, function(err, resp) {
To:
script.scripts.run({
  auth: auth,
  resource: {
    function: 'automateSheet',
    parameters: [[ // Modified
        322,
        6549.51,
        4388,
        282.98,
        454.13,
        168,
        302,
    ]],
  },
  scriptId: scriptId,
  devMode: true, // Added
}, function(err, resp) {

Modification part 3:

From:
console.log("Success");
To:
console.log(resp.data);

By this modification, you can see the response when Google Apps Script is run. About this, please decide whether it modifies this.

Note:

  • If in your Google Apps Script, you are using the function like function automateSheet(a, b, c, d, e, f, g) {}, please modify from parameters: [[322,6549.51,4388,282.98,454.13,168,302]] to parameters: [322,6549.51,4388,282.98,454.13,168,302].
  • When "File -> Project properties -> Info" at the script editor is seen, Project key is shown as Deprecated. Project key is the same with "Current API ID" shown at "Deploy as API executable". In the current stage, Project key can be used. But when it think of the future update, using Script ID might be better. This is my consideration.

Reference:

Added:

If you want to display by separating the error messages and results from Apps Script API, how about this modification?

From:

console.log(resp.data);

To:

var r = resp.data;
if ("error" in r) {
    console.log("Error: %o", r.error);
} else {
    console.log("Result: %o", r.response.result);
}

or

To:

console.log(JSON.stringify(resp.data))
halfer
  • 19,824
  • 17
  • 99
  • 186
Tanaike
  • 181,128
  • 11
  • 97
  • 165
  • Thank you for your answer, my problem was actually a simple oauth issue which I have resolved. I was using credentials from the wrong project, I can't believe how much time I wasted on that. However, when I run the script locally, it seems to just stop halfway through. I changed "Success" to resp.data and the result is `{ done: true, error: { code: 3, message: 'TypeError', details: [ [Object] ] } }` The script works when I run it through the script editor. Any tips on how to fix this? – Setheroni Sep 30 '18 at 19:22
  • @SethMcGuire I'm glad your issue was solved. But I couldn't notice about the reason of your issue. I'm really sorry for my poor skill. And then, for your new question, I updated my answer. Could you confirm it? – Tanaike Sep 30 '18 at 21:50
  • Don't be sorry, that was a tough find. Thanks for the updated answer, the error message is much more clear now. I get `errorMessage: 'Cannot read property "2" from undefined.'` – Setheroni Sep 30 '18 at 22:16
  • The line it's on comes from a for loop I have: `var values = SpreadsheetApp.getActiveSheet().getDataRange().getValues();` `for(n = 3; n < 10; n++){ btcSevenQuantity += values[n][2];` – Setheroni Sep 30 '18 at 22:17
  • I don't get this error when I run it from the script editor. Very odd – Setheroni Sep 30 '18 at 22:19
  • @SethMcGuire Thank you for your quick response. The error is due to Google Apps Script. About your Google Apps Script, that is the whole script of the function ``automateSheet()``? Unfortunately, I cannot understand about the issue from the snippet of your comment. If you can provide the script, please add it to your question, and your latest script of node.js. From them, I would like to think of the solution of your new issue. If my skill is more high, I might be able to directly resolve your issue. But my skill is not good. I'm really sorry for this situation. – Tanaike Sep 30 '18 at 22:24
  • Thank you very much. I just added those to the question. – Setheroni Sep 30 '18 at 22:35
  • @SethMcGuire Can I ask you about the line with the error? – Tanaike Sep 30 '18 at 22:36
  • @SethMcGuire And then, it seems that you don't use ``devMode: true``. In your latest node.js script, when API is called, Google Apps Script is required to update the version as I explained in my answer. So could you please try to run after added ``devMode: true``? – Tanaike Sep 30 '18 at 22:39
  • The error is in the for loops. It says values[n] is undefined. In the loop I'm just getting data from columns. – Setheroni Sep 30 '18 at 22:49
  • @SethMcGuire In your situation, when you run the script at script editor, no error occurs. When Apps Script API is used, the error occurs. Is my understanding correct? If my understanding is correct, please add ``devMode: true`` to the request body or update the version of your script. – Tanaike Sep 30 '18 at 22:52
  • That is correct. devMode: true does not seem to fix it though – Setheroni Sep 30 '18 at 23:07
  • @SethMcGuire In your script, it seems that ``devMode: true`` is not used. Is my understanding correct? – Tanaike Sep 30 '18 at 23:08
  • I have tried both of these things and still get the same error – Setheroni Sep 30 '18 at 23:13
  • @SethMcGuire And also it seems that ``parameters`` is not used in your script of node.js. Do you try to run ``automateSheet(322, 6549.51, 4388, 282.98, 454.13, 168, 302);`` in your Google Apps Script using the current script of node.js? – Tanaike Sep 30 '18 at 23:14
  • @SethMcGuire I'm really sorry for my poor English skill. I cannot understand about ``Still the same error``. What did you do? – Tanaike Sep 30 '18 at 23:20
  • I ran it through node with those parameters and added devMode: true. I still get the error – Setheroni Sep 30 '18 at 23:24
  • @SethMcGuire Did you read my latest comment? "And also it seems that ``parameters`` is not used in your script of node.js. Do you try to run ``automateSheet(322, 6549.51, 4388, 282.98, 454.13, 168, 302);`` in your Google Apps Script using the current script of node.js?" – Tanaike Sep 30 '18 at 23:25
  • @SethMcGuire Can you update your question using your latest scripts? I would like to confirm your latest situation. – Tanaike Sep 30 '18 at 23:29
  • That's okay. I'm done messing with it. Thank you for your help though – Setheroni Sep 30 '18 at 23:34
  • @SethMcGuire Although I wanted to resolve your issue, I apologize for the inconvenience caused, because of my my poor skill. – Tanaike Sep 30 '18 at 23:48
  • No need to apologize. Wanna help me with this new issue? https://stackoverflow.com/questions/52600586/cant-get-refresh-token-for-google-api-node-js – Setheroni Oct 02 '18 at 00:43