Budibase/budibase · error · HTTPError

View ${view.id} not found in table ${tableId}

Error message

View ${view.id} not found in table ${tableId}

What it means

sdk.views.update looks up the existing view in the table by view.id and requires it to exist (with a name). If the id doesn't correspond to any view on the table, an HTTPError 404 'View ${view.id} not found in table ${tableId}' is thrown rather than silently creating.

Source

Thrown at packages/server/src/sdk/workspace/views/internal.ts:76

  table.views[view.name] = view
  await db.put(table)
  return view
}

export async function update(
  tableId: string,
  view: Readonly<ViewV2>
): Promise<{ view: ViewV2; existingView: ViewV2 }> {
  const db = context.getWorkspaceDB()
  const table = await sdk.tables.getTable(tableId)
  table.views ??= {}

  const existingView = Object.values(table.views).find(
    v => isV2(v) && v.id === view.id
  )
  if (!existingView || !existingView.name) {
    throw new HTTPError(`View ${view.id} not found in table ${tableId}`, 404)
  }

  if (isV2(existingView) && existingView.type !== view.type) {
    throw new HTTPError(`Cannot update view type after creation`, 400)
  }

  view = ensureQuerySet(view)
  view = ensureQueryUISet(view)

  delete table.views[existingView.name]
  table.views[view.name] = view
  await db.put(table)
  return { view, existingView } as { view: ViewV2; existingView: ViewV2 }
}

export async function remove(viewId: string): Promise<ViewV2> {
  const db = context.getWorkspaceDB()

View on GitHub (pinned to a81a902e9a)

Solutions

  1. Call sdk.views.create instead of update if the view doesn't exist yet.
  2. Re-fetch the correct view id from the table before updating.
  3. Confirm the viewId's embedded tableId matches the table you pass.
  4. Handle the 404 and recreate the view if it was legitimately deleted.

Example fix

// before
await sdk.views.update({ id: staleId, ...view })
// after
const table = await sdk.tables.getTable(tableId)
const exists = Object.values(table.views ?? {}).some(v => isV2(v) && v.id === staleId)
if (!exists) {
  await sdk.views.create({ tableId, name: view.name, ...view })
} else {
  await sdk.views.update({ id: staleId, ...view })
}
Defensive patterns

Strategy: try-catch

Validate before calling

const table = await sdk.tables.getTable(tableId)
if (!Object.values(table.views ?? {}).some(v => isV2(v) && v.id === view.id)) {
  await sdk.views.create({ ...view, tableId })
} else {
  await sdk.views.update(view)
}

Type guard

function viewExistsInTable(view: ViewV2, table: Table): boolean {
  return Object.values(table.views ?? {}).some(v => isV2(v) && v.id === view.id)
}

Try / catch

try {
  await sdk.views.update(view)
} catch (e) {
  if (e instanceof HTTPError && e.status === 404) {
    await sdk.views.create({ ...view, tableId })
  } else { throw e }
}

Prevention

When it happens

Trigger: Calling sdk.views.update(view) where view.id is not present among table.views — view was deleted, id belongs to another table, or update was called instead of create.

Common situations: Client caches a stale view id after deletion; passing a V1 view id; tableId mismatch when moving views; race with a concurrent delete.

Understand the failure class

Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.

Related errors


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