Budibase/budibase · error · Error

Row does not exist.

Error message

Row does not exist.

What it means

update() reads the target row from the sheet first; when the row id (row number) supplied in the query does not resolve to an existing row — the sheet was modified, rows were deleted, or the id was wrong — it throws 'Row does not exist.' after logging the underlying read error. Errors from the sheets read are re-thrown as-is.

Source

Thrown at packages/server/src/integrations/googlesheets.ts:712

            const isDeprecatedSingleUser =
              type === FieldType.BB_REFERENCE &&
              subtype === BBReferenceFieldSubType.USER &&
              constraints?.type !== "array"
            if (isDeprecatedSingleUser && Array.isArray(row.get(key))) {
              row.set(key, row.get(key)[0])
            }
          }
        }
        await row.save()
        return [
          this.buildRowObject(
            sheet.headerValues,
            row.toObject(),
            row.rowNumber
          ),
        ]
      } else {
        throw new Error("Row does not exist.")
      }
    } catch (err) {
      console.error("Error reading from google sheets", err)
      throw err
    }
  }

  async delete(query: { sheet: string; rowIndex: number }) {
    await this.connect()
    const { row } = await this.getRowByIndex(query.sheet, query.rowIndex)
    if (row) {
      await row.delete()
      return [
        {
          deleted: query.rowIndex,
          [GOOGLE_SHEETS_PRIMARY_KEY]: query.rowIndex,
        },
      ]

View on GitHub (pinned to a81a902e9a)

Solutions

  1. Re-fetch the rows (READ) to get current valid row ids, then update using a fresh id
  2. Confirm the row id/rowNumber targets the correct sheet and still exists
  3. Check the console error log ('Error reading from google sheets') for the underlying cause (auth, quota, network)
  4. Handle concurrent edits — verify the sheet wasn't restructured between read and update

Example fix

// before
await integration.query({ operation: Operation.UPDATE_ROW, table: { name: 'Customers' }, id: 999 }) // stale id
// after
const rows = await integration.query({ operation: Operation.READ, table: { name: 'Customers' } })
const target = rows.find(r => r.key === 'someValue')
await integration.query({ operation: Operation.UPDATE_ROW, table: { name: 'Customers' }, id: target.id, body: { Name: 'New' } })
Defensive patterns

Strategy: retry

Validate before calling

async function refreshRowId(integration, tableName, matchFn) {
  const rows = await integration.query({ operation: 'READ', table: { name: tableName } })
  const fresh = rows.find(matchFn)
  if (!fresh) throw new Error('Row not found after re-fetch')
  return fresh.id
}

Try / catch

try {
  await integration.query({ operation: 'UPDATE_ROW', id: rowId, ... })
} catch (err) {
  if (err.message === 'Row does not exist.') {
    // re-fetch rows for a fresh id, then retry the update once
  } else throw err
}

Prevention

When it happens

Trigger: Calling query({ operation: 'UPDATE_ROW' }) with an id/rowNumber that no longer exists in the sheet, or the read from google-spreadsheet fails/returns nothing for that id.

Common situations: Stale row ids cached from an earlier fetch after rows were deleted or the sheet reordered; concurrent edits by another user; wrong sheet targeted so the row number is out of range; API quota/network error surfaced through the same catch block.

Understand the failure class

Background: Record Not Found Errors: "not found", RecordNotFound, and "was not found" — what they mean and how to fix them — this error's family across 28 libraries.

Related errors


AI-assisted analysis of Budibase/budibase@a81a902e9a (2026-08-29). Data as JSON: /api/errors/877a2bb5f8f6ff72. Report an issue: GitHub.