Budibase/budibase · error · HTTPError

App with name '${name}' is already taken.

Error message

App with name '${name}' is already taken.

What it means

guardName enforces unique workspace app names within a workspace, comparing normalized (case/whitespace-insensitive) names. When creating a new workspace app, or renaming one during update, if any other app (different _id) has the same normalized name, a 400 HTTPError is thrown.

Source

Thrown at packages/server/src/sdk/workspace/workspaceApps/crud.ts:18

import { context, docIds, events, HTTPError } from "@budibase/backend-core"
import { RequiredKeys, WithoutDocMetadata, WorkspaceApp } from "@budibase/types"
import sdk from "../.."
import { helpers } from "@budibase/shared-core"
import { getValidProjectIdsForDuplication } from "../projects/utils"

async function guardName(name: string, id?: string) {
  const existingWorkspaceApps = await fetch()
  const normalizedName = helpers.normalizeForComparison(name)

  if (
    existingWorkspaceApps.find(
      app =>
        helpers.normalizeForComparison(app.name) === normalizedName &&
        app._id !== id
    )
  ) {
    throw new HTTPError(`App with name '${name}' is already taken.`, 400)
  }
}

const duplicateScreens = async (originalAppId: string, newAppId: string) => {
  const screens = await sdk.screens.fetch()

  const appScreens = screens.filter(s => s.workspaceAppId === originalAppId)
  const newScreens = []
  for (let i = 0; i < appScreens.length; i++) {
    const screen = appScreens[i]
    const createdScreen = await sdk.screens.create({
      ...{
        layoutId: screen.layoutId,
        showNavigation: screen.showNavigation,
        width: screen.width,
        routing: screen.routing,
        props: screen.props,
        name: screen.name,

View on GitHub (pinned to a81a902e9a)

Solutions

  1. Fetch existing workspace apps (sdk.workspaceApps.fetch()) and pick a unique name before create/rename
  2. Rely on helpers.duplicateName (as duplicate() does) to auto-suffix the name, e.g. 'Admin 2'
  3. Catch the 400 and retry with a suffixed name

Example fix

// before
await sdk.workspaceApps.create({ name: desiredName, url: `/${slug}` })
// after
const existing = await sdk.workspaceApps.fetch()
const name = helpers.duplicateName(desiredName, existing.map(a => a.name))
await sdk.workspaceApps.create({ name, url: `/${slugify(name)}` })
Defensive patterns

Strategy: validation

Validate before calling

const existing = await sdk.workspaceApps.fetch()
const normalized = name.trim().toLowerCase()
if (existing.some(a => a.name.trim().toLowerCase() === normalized && a._id !== id)) {
  throw new Error(`Name '${name}' already in use`)
}

Try / catch

try {
  await sdk.workspaceApps.create(app)
} catch (e) {
  if (e?.status === 400 && e?.message?.includes('already taken')) {
    const names = (await sdk.workspaceApps.fetch()).map(a => a.name)
    await sdk.workspaceApps.create({ ...app, name: helpers.duplicateName(app.name, names) })
  } else throw e
}

Prevention

When it happens

Trigger: Calling sdk.workspaceApps.create with a name matching an existing app; calling update with a name that differs from the persisted name and collides (after normalization) with another app in the same workspace DB.

Common situations: Renaming 'Admin' to 'admin' when 'admin' exists; trailing-space or casing variants of an existing name; automation scripts that create workspace apps without checking fetch() first; concurrent creations racing past the uniqueness check.

Related errors


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