Budibase/budibase · error

Cannot fetch row by ID "${rowId}"

Error message

Cannot fetch row by ID "${rowId}"

What it means

getRow fetches a single external-datasource row by issuing a READ query with an equality filter on the row ID. The query executed without error but returned an empty array, meaning no row matched the given ID, so the method throws. It signals a row-not-found condition for external (SQL) datasources.

Source

Thrown at packages/server/src/api/controllers/row/ExternalRequest.ts:303

  }

  getTable(tableId: string | undefined): Table | undefined {
    if (!tableId) {
      throw new Error("Table ID is unknown, cannot find table")
    }
    const { tableName } = breakExternalTableId(tableId)
    return this.tables[tableName]
  }

  async getRow(table: Table, rowId: string): Promise<Row> {
    const response = await makeExternalQuery({
      endpoint: getEndpoint(table._id!, Operation.READ),
      filters: this.prepareFilters(rowId, {}, table),
    })
    if (Array.isArray(response) && response.length > 0) {
      return response[0]
    } else {
      throw new Error(`Cannot fetch row by ID "${rowId}"`)
    }
  }

  inputProcessing<T extends Row | undefined>(
    row: T,
    table: Table
  ): { row: T; manyRelationships: ManyRelationship[] } {
    if (!row) {
      return { row, manyRelationships: [] }
    }
    // we don't really support composite keys for relationships, this is why [0] is used
    // @ts-ignore
    const tablePrimary: string = table.primary[0]
    let newRow: Row = {},
      manyRelationships: ManyRelationship[] = []
    for (let [key, field] of Object.entries(table.schema)) {
      // if set already, or not set just skip it
      if (row[key] === undefined || newRow[key]) {

View on GitHub (pinned to a81a902e9a)

Solutions

  1. Verify the rowId matches an actual primary-key value in the external table
  2. Check the table's primary key configuration in the datasource and re-sync the table schema
  3. Guard deletes/updates with an existence check (query with filters) before calling getRow

Example fix

// before
const row = await externalRequest.getRow(table, rowId)
// after
const existing = await makeExternalQuery({ endpoint: getEndpoint(table._id!, Operation.READ), filters: { equal: { id: rowId } } })
if (!Array.isArray(existing) || existing.length === 0) {
  throw new HTTPError(`Row ${rowId} not found in table ${table._id}`, 404)
}
const row = existing[0]
Defensive patterns

Strategy: try-catch

Validate before calling

const matches = await makeExternalQuery({ endpoint: getEndpoint(table._id!, Operation.READ), filters: { equal: { [primaryKey]: rowId } } })
if (!Array.isArray(matches) || matches.length === 0) throw new HTTPError(404)

Try / catch

try {
  const row = await externalRequest.getRow(table, rowId)
} catch (err) {
  if ((err as Error).message.startsWith("Cannot fetch row by ID")) {
    throw new HTTPError(`Row ${rowId} not found`, 404)
  }
  throw err
}

Prevention

When it happens

Trigger: Calling row(), beforeRow() or any update/delete flow that pre-fetches a row, with a rowId that does not exist in the backing SQL table; primary-key mismatch (e.g. passing an _id in Budibase internal format rather than the actual primary key value).

Common situations: Client holding a stale ID after the row was deleted externally; querying by a Budibase internal ID against a SQL table whose PK differs; string/number type mismatch in the filter (e.g. "1" vs 1); another process removed the row concurrently.

Related errors


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