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

Thrown by the view V2 update path for external (SQL) datasources when the view id being updated cannot be matched against any V2 view stored on the target table. The update flow loads the datasource document, looks up an existing view by id in table.views, and 404s if it is absent or has no name, because update is only meaningful for an existing view.

Source

Thrown at packages/server/src/sdk/workspace/views/external.ts:88

  return view
}

export async function update(
  tableId: string,
  view: Readonly<ViewV2>
): Promise<{ view: Readonly<ViewV2>; existingView: ViewV2 }> {
  const db = context.getWorkspaceDB()

  const { datasourceId, tableName } = breakExternalTableId(tableId)
  const ds = await sdk.datasources.get(datasourceId)
  ds.entities![tableName].views ??= {}
  const views = ds.entities![tableName].views!

  const existingView = Object.values(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 views[existingView.name]
  views[view.name] = view
  await db.put(ds)
  return { view, existingView } as { view: ViewV2; existingView: ViewV2 }
}

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

  const view = await get(viewId)

View on GitHub (pinned to a81a902e9a)

Solutions

  1. Fetch the view first (sdk.views.get / getEnriched) and confirm the id exists before updating
  2. Verify the view id belongs to the same external tableId passed to update
  3. Use create instead of update when the view does not yet exist
  4. Refresh client state and retry with a freshly loaded view

Example fix

// before
await sdk.views.update(tableId, { id: "view_stale", ...view })
// after
const existing = await sdk.views.get(view.id).catch(() => null)
if (!existing) view = await sdk.views.create(tableId, view)
else await sdk.views.update(tableId, view)
Defensive patterns

Strategy: validation

Validate before calling

const views = Object.values(ds.entities[tableName]?.views ?? {}).filter(isV2)
if (!views.some(v => v.id === view.id)) throw new Error(`View ${view.id} missing; create it instead`)

Type guard

function isV2View(v: unknown): v is ViewV2 {
  return typeof v === "object" && v !== null && "id" in v && (v as ViewV2).version === 2
}

Try / catch

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

Prevention

When it happens

Trigger: Calling sdk.views.external.update (or the PUT view API) with a tableId and a ViewV2 whose id does not exist in ds.entities[tableName].views, the view is a V1 legacy view (filtered out by isV2), or the view was deleted by another request before the update.

Common situations: Stale client state updating a view deleted in another tab/session; using an internal-table view id against an external table; passing a fabricated or wrong view id; datasource document regenerated so view ids no longer match.

Related errors


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