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 | undefinedView on GitHub (pinned to a81a902e9a)
Solutions
- Inspect the HTTPError status (400 vs 500) and message to identify the underlying save() failure
- Log the original err payload server-side before it is coerced, since the string form loses structure
- Ensure inner layers throw Error or HTTPError instances so the 400 branch is taken with the real message
- 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
- Throw Error/HTTPError instances in custom code, never raw strings
- Log original error payloads before they are coerced into HTTPError
- Check HTTPError status to distinguish validation (400) from server (500) issues
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
- OIDC Config contents invalid
- Koa context must be supplied to logout.
- Unexpected header format
- Unexpected response when fetching openid-configuration: ${re
- unexpected response ${response.statusText}
AI-assisted analysis of Budibase/budibase@a81a902e9a (2026-08-29).
Data as JSON: /api/errors/f47e465004879871.
Report an issue: GitHub.