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
- Re-fetch the rows (READ) to get current valid row ids, then update using a fresh id
- Confirm the row id/rowNumber targets the correct sheet and still exists
- Check the console error log ('Error reading from google sheets') for the underlying cause (auth, quota, network)
- 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
- Always use row ids from a recent READ, never cached across sessions
- Re-read the sheet after any delete/reorder before updating rows
- Watch the logged 'Error reading from google sheets' output to distinguish not-found from auth/quota failures
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
- Selected endpoint could not be imported
- Custom REST template not found
- Cannot fetch row by ID "${rowId}"
- You must set a spreadsheet ID in your configuration to fetch
- Error authenticating with google sheets. ${json.error_descri
AI-assisted analysis of Budibase/budibase@a81a902e9a (2026-08-29).
Data as JSON: /api/errors/877a2bb5f8f6ff72.
Report an issue: GitHub.