Budibase/budibase · error · Error

${readableMessage}

Error message

${readableMessage}

What it means

MySQL integration's internalQuery() wraps the callback-based mysql client in a promise and maps driver errors via getReadableErrorMessage(SourceName.MYSQL, err.errno). When errno matches a known MySQL error code, it throws a new Error with the readable message and attaches the original driver error as cause; otherwise it rethrows the original error untouched.

Source

Thrown at packages/server/src/integrations/mysql.ts:281

      disableCoercion: false,
    }
  ): Promise<any[] | any> {
    try {
      if (opts?.connect) {
        await this.connect()
      }
      const baseBindings = query.bindings || []
      const bindings = opts?.disableCoercion
        ? baseBindings
        : bindingTypeCoerce(baseBindings)
      this.log(query.sql, bindings)
      // Node MySQL is callback based, so we must wrap our call in a promise
      const response = await this.client!.query(query.sql, bindings)
      return response[0]
    } catch (err: any) {
      let readableMessage = getReadableErrorMessage(SourceName.MYSQL, err.errno)
      if (readableMessage) {
        throw new Error(readableMessage, { cause: err })
      } else {
        throw err
      }
    } finally {
      if (opts?.connect && this.client) {
        await this.disconnect()
      }
    }
  }

  async buildSchema(
    datasourceId: string,
    entities: Record<string, Table>
  ): Promise<Schema> {
    const tables: { [key: string]: Table } = {}
    await this.connect()

    try {

View on GitHub (pinned to a81a902e9a)

Solutions

  1. Read the readableMessage — it is the mapped MySQL error (e.g. duplicate entry) — and fix the SQL or data
  2. For ER_DUP_ENTRY, deduplicate or use INSERT ... ON DUPLICATE KEY UPDATE / INSERT IGNORE
  3. For access-denied errors, verify credentials and grant privileges / allow the host
  4. Inspect error.cause for the full driver error including the raw errno and SQL state

Example fix

// before: duplicate key on re-insert
INSERT INTO users (email) VALUES ('a@example.com')
// after
INSERT INTO users (email) VALUES ('a@example.com') ON DUPLICATE KEY UPDATE email = VALUES(email)
Defensive patterns

Strategy: try-catch

Validate before calling

// pre-flight: ensure table exists before inserting
const tables = await ds.getTableNames?.() ?? []
if (!tables.includes(tableName)) {
  throw new Error(`Table ${tableName} does not exist in MySQL database`)
}

Type guard

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

Try / catch

try {
  return await ds.query(sqlQuery)
} catch (err) {
  const cause = (err as any).cause
  if (cause?.errno === 1062) {
    // duplicate entry: retry with a different key or upsert
  } else if (cause?.errno === 1146) {
    // missing table: fix schema
  } else if (cause?.errno === 1045) {
    // access denied: fix credentials
  }
  throw err
}

Prevention

When it happens

Trigger: Any MySQL error during query.sql execution whose err.errno is in the mapping — classically ER_DUP_ENTRY (1062) duplicate key, ER_NO_SUCH_TABLE (1146), ER_ACCESS_DENIED_ERROR (1045), syntax errors (1064), and connection losses.

Common situations: Inserting a duplicate unique key; querying a table that doesn't exist or is in another database; wrong credentials or host not whitelisted for the MySQL user; malformed SQL from dynamic query building; connection dropped by wait_timeout.

Related errors


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