Budibase/budibase · error · HTTPError
Cannot update view type after creation
Error message
Cannot update view type after creation
What it means
sdk.views.update rejects changing a view's type after creation: if the existing V2 view's type differs from the incoming view's type, an HTTPError 400 'Cannot update view type after creation' is thrown. View type (e.g. table vs calculation) is immutable; delete and recreate instead.
Source
Thrown at packages/server/src/sdk/workspace/views/internal.ts:80
}
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()
const view = await get(viewId)
const table = await sdk.tables.getTable(view?.tableId)
if (!view) {
throw new HTTPError(`View ${viewId} not found`, 404)View on GitHub (pinned to a81a902e9a)
Solutions
- Delete the existing view and create a new one with the desired type.
- Omit or keep view.type identical to the existing view's type in the update payload.
- Copy the view config into a new calculation view if aggregation behavior is needed.
Example fix
// before
await sdk.views.update({ id: viewId, type: "calculation", ...rest })
// after
await sdk.views.remove(viewId)
await sdk.views.create({ tableId, name: "myView", type: "calculation", ...rest }) Defensive patterns
Strategy: validation
Validate before calling
const existing = Object.values(table.views ?? {}).find(v => isV2(v) && v.id === view.id)
if (existing && existing.type !== view.type) {
await sdk.views.remove(view.id)
await sdk.views.create({ tableId, name: existing.name, ...view })
} else {
await sdk.views.update(view)
} Type guard
function canUpdateType(existing: ViewV2, next: ViewV2): boolean {
return existing.type === next.type
} Try / catch
try {
await sdk.views.update(view)
} catch (e) {
if (e instanceof HTTPError && e.status === 400 && e.message === "Cannot update view type after creation") {
await sdk.views.remove(view.id)
await sdk.views.create({ tableId, ...view })
} else { throw e }
} Prevention
- Treat view.type as immutable after creation
- Strip or freeze the type field in generic update payloads
- Recreate views instead of mutating their type
When it happens
Trigger: Calling sdk.views.update with view.type different from the stored view's type — e.g. converting a standard view to a calculation view via update.
Common situations: UI/API flows that convert a view between types with a PUT-style update; shared form payload that includes a type field the user changed; generic update code that always sends the full object including type.
Related errors
- View name is required
- 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/e0f91e862d659dc3.
Report an issue: GitHub.