Budibase/budibase · error · HTTPError
Cannot update view type after creation
Error message
Cannot update view type after creation
What it means
Thrown when an update payload tries to change the type of an existing V2 view (e.g. table -> calculation). View type is immutable after creation because the query/schema shape differs fundamentally between types, so the SDK rejects type-flipping updates with a 400.
Source
Thrown at packages/server/src/sdk/workspace/views/external.ts:91
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)
if (!view) {
throw new HTTPError(`View ${viewId} not found`, 404)View on GitHub (pinned to a81a902e9a)
Solutions
- Send the update with the same type as the existing view
- Delete the view and create a new one of the desired type
- Fix the client so the type selector is disabled when editing an existing view
Example fix
// before
await sdk.views.update(tableId, { ...view, type: ViewV2Type.CALCULATION })
// after
const existing = await sdk.views.get(view.id)
await sdk.views.update(tableId, { ...view, type: existing.type })
// or: await sdk.views.remove(view.id); await sdk.views.create(tableId, calcView) Defensive patterns
Strategy: validation
Validate before calling
const existing = await sdk.views.get(view.id)
if (existing.type !== view.type) throw new Error("View type change requires delete+create") Type guard
function sameType(existing: ViewV2, next: ViewV2): boolean { return existing.type === next.type } Try / catch
try { await sdk.views.update(tableId, view) }
catch (e) {
if (e instanceof HTTPError && e.status === 400 && /view type/i.test(e.message)) {
await sdk.views.remove(view.id)
await sdk.views.create(tableId, view)
} else throw e
} Prevention
- Treat view type as immutable; disable the type selector when editing
- Recreate views instead of mutating type
- Never merge payloads across view kinds
- Read the existing view first and preserve its type
When it happens
Trigger: Calling update with a ViewV2 whose id matches an existing view but whose type property differs from existingView.type, e.g. sending a calculation view payload over an existing table view id.
Common situations: Frontend form reused across view types sends the wrong type; a migration script rewrites view.type; copy/pasted payloads between view kinds.
Related errors
- View '${viewId}' not found in '${tableId}'
- Cannot insert rows through a calculation view
- Duplicate calculation on field "${schema.field}", calculatio
- Duplicate calculation on field "${schema.field}", calculatio
- Calculation views can only have a maximum of 5 fields
AI-assisted analysis of Budibase/budibase@a81a902e9a (2026-08-29).
Data as JSON: /api/errors/6d3b7e0fde748409.
Report an issue: GitHub.