Budibase/budibase · error · HTTPError

err.message || err (rethrown as HTTPError)

Error message

err.message || err (rethrown as HTTPError)

What it means

This is the catch-all in the internal table create() wrapper: when save() throws something that is not an Error instance (e.g. a plain string or a bare object from CouchDB/pouch), it is rethrown as an HTTPError using err.message || err with err.status or a 500 fallback. It exists so non-Error throwables from lower layers still surface as a proper HTTP response.

Source

Thrown at packages/server/src/sdk/workspace/tables/internal/index.ts:63

  const isImport = !!rows

  if (!tableToSave.views) {
    tableToSave.views = {}
  }

  try {
    const { table } = await save(tableToSave, {
      userId,
      rowsToImport: rows,
      isImport,
    })

    return table
  } catch (err: any) {
    if (err instanceof Error) {
      throw new HTTPError(err.message, 400)
    } else {
      throw new HTTPError(err.message || err, err.status || 500)
    }
  }
}

export async function save(
  table: Table,
  opts?: {
    userId?: string
    tableId?: string
    rowsToImport?: Row[]
    renaming?: RenameColumn
    isImport?: boolean
  }
) {
  const db = context.getWorkspaceDB()

  // if the table obj had an _id then it will have been retrieved
  let oldTable: Table | undefined

View on GitHub (pinned to a81a902e9a)

Solutions

  1. Inspect the HTTPError status (400 vs 500) and message to identify the underlying save() failure
  2. Log the original err payload server-side before it is coerced, since the string form loses structure
  3. Ensure inner layers throw Error or HTTPError instances so the 400 branch is taken with the real message
  4. Upgrade backend-core/pouch layers if known bugs throw raw objects

Example fix

// before (inner code)
throw 'Table validation failed'
// after
throw new Error('Table validation failed')
Defensive patterns

Strategy: try-catch

Validate before calling

// ensure inner layers throw Errors
function assertError(e: unknown): e is Error { return e instanceof Error }

Type guard

function isError(e: unknown): e is Error {
  return e instanceof Error
}

Try / catch

try {
  await sdk.tables.create(payload)
} catch (e) {
  if (isError(e)) {
    // e.message is the real validation message, status 400
  } else {
    // coerced non-Error throw; log raw value
  }
}

Prevention

When it happens

Trigger: Calling sdk.tables.create (POST /tables) when an inner save() call throws a non-Error value, such as a PouchDB/CouchDB error object that lacks Error.prototype or a thrown string, instead of the standard Error instances thrown by validation.

Common situations: CouchDB/PouchDB layer throwing non-standard error payloads; older plugin or integration code throwing strings; proxied DB errors surfacing as plain objects.

Related errors


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