Budibase/budibase · error · Error

${readableMessage}

Error message

${readableMessage}

What it means

internalQuery() runs SQL against SQL Server and maps driver errors to human-readable messages via getReadableErrorMessage(SourceName.SQL_SERVER, err.number). When the error's number matches a known SQL Server error code, it throws a new Error with that readable message, chaining the original error as cause.

Source

Thrown at packages/server/src/integrations/microsoftSqlServer.ts:388

        for (let binding of query.bindings) {
          request.input(`p${count++}`, binding)
        }
      }
      // this is a hack to get the inserted ID back,
      //  no way to do this with Knex nicely
      const sql =
        operation === Operation.CREATE
          ? `${query.sql}; SELECT SCOPE_IDENTITY() AS id;`
          : query.sql
      this.log(sql, query.bindings)
      return await request.query(sql)
    } catch (err: any) {
      let readableMessage = getReadableErrorMessage(
        SourceName.SQL_SERVER,
        err.number
      )
      if (readableMessage) {
        throw new Error(readableMessage, { cause: err })
      } else {
        throw new Error(err.message as string, { cause: err })
      }
    }
  }

  getDefinitionSQL(tableName: string, schemaName: string): SqlQuery {
    return {
      sql: `select *
            from INFORMATION_SCHEMA.COLUMNS
            where TABLE_NAME=@p0 AND TABLE_SCHEMA=@p1`,
      bindings: [tableName, schemaName],
    }
  }

  getConstraintsSQL(tableName: string, schemaName: string): SqlQuery {
    return {
      sql: `SELECT * FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS AS TC

View on GitHub (pinned to a81a902e9a)

Solutions

  1. Read the readableMessage — it is the mapped SQL Server error — and fix the SQL or data it describes
  2. For duplicate-key errors, deduplicate rows or change the conflicting key values
  3. For permission errors, grant the SQL user the required privileges
  4. If the message is unhelpful, inspect error.cause for the original tedious error with full details

Example fix

// before: duplicate key from re-running an insert
INSERT INTO users (id, email) VALUES (1, 'a@example.com')
// after: make it idempotent
IF NOT EXISTS (SELECT 1 FROM users WHERE id = 1) INSERT INTO users (id, email) VALUES (1, 'a@example.com')
Defensive patterns

Strategy: try-catch

Validate before calling

// validate before executing: check the target object exists
const exists = await internalQuery({ sql: "SELECT 1 FROM sys.tables WHERE name = @tableName", bindings: [tableName] })
if (!exists.length) throw new Error(`Table ${tableName} does not exist in SQL Server`)

Type guard

function hasSqlNumber(err: unknown): err is { number: number; message: string } {
  return typeof err === "object" && err !== null && typeof (err as any).number === "number"
}

Try / catch

try {
  return await ds.query(sqlQuery)
} catch (err) {
  // message is the mapped readable message; original tedious error is on cause
  if (/duplicate key/i.test(err.message)) {
    // handle conflict: retry with new key or upsert
  }
  console.error("SQL Server error:", (err as any).cause)
  throw err
}

Prevention

When it happens

Trigger: Any SQL Server error raised during query execution whose err.number is in the readable-message mapping — most commonly constraint violations (duplicate key), syntax errors, permission denials, or missing object errors returned by tedious.

Common situations: Running a query that inserts a duplicate primary key or violates a foreign key; referencing a nonexistent table/column; a user without SELECT/INSERT permissions; malformed SQL generated by the query builder.

Related errors


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