Combining table cells conditionally in Google Docs using API requests

I’m working with a Google Docs file that contains several tables. Each table has two header rows and four columns. The number of data rows varies between tables, and the last column might have an image or be empty depending on form submissions from our app.

What I need to do is go through every table and check if the last column is empty. When there’s no image in that column, I want to combine it with the column right before it.

I tried using the basic merge() function first but ran into issues. Then I found some code online and modified it, but now I keep getting this error: ‘GoogleJsonResponseException: API call to docs.documents.batchUpdate failed with error: Invalid requests[0].mergeTableCells: The provided table start location is invalid.’

Here’s what I have so far:

// create copy of template document
var duplicateFile = sourceTemplate.makeCopy(reportName, targetFolder);
var docToModify = DocumentApp.openById(duplicateFile.getId());
var documentBody = docToModify.getBody();

function combineCells() {
  var tableCounter = 0;
  documentBody.getTables().forEach(processTable);

  function processTable(currentTable) {
    if (tableCounter > 2) { // skip first 3 tables
      var rowNum = 2; // start after header rows
      
      for (rowNum; rowNum < currentTable.getNumRows(); rowNum++) {
        if (currentTable.getCell(rowNum, 3).getText() == "") {
          var tablePosition = documentBody.getChildIndex(currentTable);
          var apiRequests = [
            {
              mergeTableCells: {
                tableRange: {
                  tableCellLocation: {
                    tableStartLocation: {
                      index: tablePosition,
                    },
                    rowIndex: rowNum,
                    columnIndex: 3,
                  },
                  "rowSpan": 1,
                  "columnSpan": 2
                },
              },
            },
          ];
          
          Docs.Documents.batchUpdate({ requests: apiRequests }, duplicateFile.getId());
        }
      }
    }
    tableCounter++;
  }
}

Can someone help me figure out how to properly identify where each table starts? I think that’s what’s causing the invalid location error.

Had this exact problem last year with contract automation. It’s not just your index calculation - you’re hammering the API with multiple batchUpdate calls without waiting for them to finish.

Docs API needs time to process each merge before you can reference the updated structure. I fixed it by batching all merge operations for each table into one request instead of calling batchUpdate for every single row.

Grab all the cells that need merging first, then build one apiRequests array with all the mergeTableCells operations for that table. Kills the race condition and cuts way down on API calls.

One more thing - when you merge cells, the row structure changes instantly so your loop index gets screwed up. I had to either work backwards through rows or recalculate row counts after each table change to avoid skipping rows.

Make sure you’re using the right document ID in your batchUpdate call. You’re passing duplicateFile.getId() but working with docToModify - they should match, but cache issues sometimes mess this up. Try adding a 100ms delay between operations too. The API needs time to catch up.

The problem happens because Google Docs rebuilds the table internally after each merge, which breaks your index references in the same batch. I fixed this by processing tables in reverse order and working backwards through rows. That way, changes to later elements don’t mess up earlier positions. Also, collect all empty cells per table first, then run one batchUpdate call per table instead of per row. What worked even better for me was switching to insertTable after figuring out what needed merging. Just delete the original table and insert a new one with the right cell structure. More work upfront, but you skip all the index tracking headaches when you’re dealing with multiple dynamic tables.

you’re calling getChildIndex on the wrong object. get the table start position from the document, not the body element. also refresh your document reference after each batchUpdate call - the doc structure changes when you merge cells. i had the same problem and using Docs.Documents.get() to refetch between operations fixed it.

Been there with Google Docs API headaches. Character index calculation works but gets messy fast with multiple tables and dynamic content.

I switched to Latenode after wrestling with the same issues. Set up a flow that connects to Google Docs, scans tables automatically, and handles cell merging without the index drama.

You don’t worry about refreshing document references or tracking character positions. Configure when cells should merge (empty last column for you) and let it run.

I use it for report generation where table structures change based on form data. Way cleaner than maintaining API request code, especially when scaling across different templates.

Check it out: https://latenode.com

You’re mixing DocumentApp methods with the raw Docs API incorrectly. When you use documentBody.getChildIndex(currentTable), you get the element index but the API requires a character position index. I encountered the same issue and resolved it by calculating the character position manually. You should loop through all elements before your target table and sum their text lengths. Here’s a function that worked for me: javascript function getTableCharacterIndex(body, targetTable) { var elements = body.getNumChildren(); var charIndex = 1; for (var i = 0; i < elements; i++) { var element = body.getChild(i); if (element === targetTable) { return charIndex; } charIndex += element.asText().getText().length + 1; } return charIndex; } Then, replace your line with var tablePosition = getTableCharacterIndex(documentBody, currentTable);. This will provide the correct character position for the API’s tableStartLocation.

I hit this exact problem a few months ago building a report generator. The issue is getChildIndex() gives you the wrong index for the API call - it returns the position within document body elements, but the Docs API wants the actual character index where the table starts.

I fixed it by using getOffsetIndex() instead. Change your tablePosition line to:

var tablePosition = documentBody.getOffsetIndex(currentTable);

You’ve got another problem in your merge logic too. To merge the last column with the previous one, start from column 2 (third column) and span 2 columns to cover both column 2 and 3. Right now you’re starting from column 3 which only leaves one column to span.

Update your tableCellLocation:

tableCellLocation: {
  tableStartLocation: {
    index: tablePosition,
  },
  rowIndex: rowNum,
  columnIndex: 2, // start from column 2 instead of 3
},

This’ll fix both the invalid location error and actually merge the cells you want.