mysql2
semver
>=3.9.8postconditions28functions15last verified2026-04-18coverage score79%Postconditions — what we check
- connect · connection-failureerrorWhenCannot connect (wrong credentials, host unreachable, etc.)Throws
Error with code 'ECONNREFUSED', 'ER_ACCESS_DENIED_ERROR', etc.Required handlingCaller MUST catch connection errors. Common error codes: - ECONNREFUSED: MySQL server not running - ER_ACCESS_DENIED_ERROR: Wrong username/password - ETIMEDOUT: Network timeout Implement retry with exponential backoff for transient issues.costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisibleSources[1] - query · syntax-errorerrorWhenSQL syntax errorThrows
Error with code 'ER_PARSE_ERROR' or similarRequired handlingCaller MUST validate SQL syntax before execution. DO NOT retry - indicates SQL syntax error. Check error.sqlMessage for details.costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisibleSources[1] - query · constraint-violationerrorWhenUnique constraint, foreign key, or NOT NULL violationThrows
Error with code 'ER_DUP_ENTRY', 'ER_NO_REFERENCED_ROW', 'ER_BAD_NULL_ERROR'Required handlingCaller MUST handle constraint violations: - ER_DUP_ENTRY (1062): Duplicate key violation - ER_NO_REFERENCED_ROW_2 (1452): Foreign key constraint - ER_BAD_NULL_ERROR (1048): NOT NULL constraint Extract details from error.sqlMessage. DO NOT retry without fixing data.costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisibleSources[2] - query · connection-errorerrorWhenConnection lost during query executionThrows
Error with code 'PROTOCOL_CONNECTION_LOST', 'ECONNRESET'Required handlingCaller MUST handle connection errors. Connection may be lost due to timeout or server restart. Implement retry with exponential backoff.costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisibleSources[1] - query · table-not-founderrorWhenTable or view does not existThrows
Error with code 'ER_NO_SUCH_TABLE' (1146)Required handlingCaller MUST verify table exists before querying. DO NOT retry - indicates schema mismatch or missing migration.costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisibleSources[2] - execute · syntax-errorerrorWhenSQL syntax error in prepared statementThrows
Error with code 'ER_PARSE_ERROR' or similarRequired handlingCaller MUST validate SQL syntax. Prepared statements prevent SQL injection but not syntax errors. DO NOT retry - fix SQL syntax.costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisibleSources[1] - execute · constraint-violationerrorWhenUnique, foreign key, or NOT NULL constraint violationThrows
Error with code 'ER_DUP_ENTRY', 'ER_NO_REFERENCED_ROW_2', 'ER_BAD_NULL_ERROR'Required handlingCaller MUST handle constraint violations gracefully. DO NOT retry without changing violating data.costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisibleSources[2] - execute · connection-errorerrorWhenConnection lost or timeoutThrows
Error with code 'PROTOCOL_CONNECTION_LOST', 'ETIMEDOUT'Required handlingCaller MUST handle connection errors. May be transient - implement retry logic.costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisibleSources[1] - getConnection · pool-exhaustederrorWhenAll connections in pool are busy and timeout exceededThrows
Error with message 'Pool is closed' or timeout errorRequired handlingCaller MUST handle pool exhaustion. Root causes: 1. Connections not released (forgot connection.release()) 2. Pool size too small for load 3. Queries taking too long ALWAYS release connections in finally block.costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisibleSources[1] - getConnection · connection-failureerrorWhenCannot establish connection from poolThrows
Error with code 'ECONNREFUSED', 'ETIMEDOUT'Required handlingCaller MUST handle connection failures. Implement retry with exponential backoff.costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisibleSources[1] - beginTransaction · transaction-start-failureerrorWhenCannot start transaction (connection error, etc.)Throws
Error with connection-related codesRequired handlingCaller MUST catch transaction start errors. Network errors may be transient and retriable.costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisibleSources[1] - commit · commit-failureerrorWhenTransaction commit fails (constraint violation, connection lost, etc.)Throws
Error with various codes depending on failure reasonRequired handlingCaller MUST catch commit errors. If commit fails, transaction is rolled back. Caller should handle rollback appropriately.costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisibleSources[1] - rollback · rollback-failureerrorWhenRollback fails (connection lost, etc.)Throws
Error with connection-related codesRequired handlingCaller MUST catch rollback errors. Even rollback can fail due to connection issues. Log rollback failures for investigation.costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisibleSources[1] - createConnection · createconnection-host-unreachableerrorWhenMySQL server is not running, host is wrong, or port is unreachableThrows
Promise rejects with Error: ECONNREFUSED (connect ECONNREFUSED 127.0.0.1:3306)Required handlingWrap await createConnection() in try-catch. Handle ECONNREFUSED at application startup and retry with backoff. In containerized deployments, add readiness checks before attempting connection.costhighin prodimmediate exceptionusers seeservice unavailablevisibilityvisibleSources[3] - createConnection · createconnection-access-deniederrorWhenUsername, password, or database name is not authorized on the MySQL serverThrows
Promise rejects with Error: ER_ACCESS_DENIED_ERROR (MySQL errno 1045, SQLSTATE 28000)Required handlingWrap await createConnection() in try-catch. Check err.code === 'ER_ACCESS_DENIED_ERROR' and log a clear error message. Verify credentials in environment variables before deployment. Do not expose err.message to users.costhighin prodimmediate exceptionusers seeservice unavailablevisibilityvisibleSources[3] - createConnection · createconnection-too-many-connectionserrorWhenMySQL server has reached its max_connections limitThrows
Promise rejects with Error: ER_CON_COUNT_ERROR (MySQL errno 1040, SQLSTATE 08004)Required handlingWrap await createConnection() in try-catch. Implement connection pooling (use createPool() instead of createConnection() per-request) to avoid exhausting the server's connection limit. Retry with exponential backoff when ER_CON_COUNT_ERROR.costmediumin prodimmediate exceptionusers seeservice unavailablevisibilitysilentSources[3] - end · end-pool-connection-errorwarningWhenPool.end() called while queries are in-flight or connections fail to closeThrows
Promise rejects with PROTOCOL_CONNECTION_LOST or connection close errorRequired handlingWrap pool.end() or connection.end() in try-catch in shutdown handlers (SIGTERM, process.on('exit')). Log the error but do not re-throw — the goal is graceful shutdown, not failing on cleanup.costlowin prodimmediate exceptionusers seeservice unavailablevisibilitysilentSources[4] - end · end-called-on-closed-connectionwarningWhenpool.end() called while pool.getConnection() calls are still pendingThrows
Pending getConnection() calls receive Error: 'Pool is closed.'Required handlingDrain in-flight requests before calling pool.end(). In graceful shutdown, stop accepting new requests first, then wait for in-flight handlers to complete, then call pool.end(). Wrap pool.end() in try-catch.costlowin prodimmediate exceptionusers seeservice unavailablevisibilityvisibleSources[4] - prepare · prepare-sql-syntax-errorerrorWhenSQL template passed to prepare() has a syntax errorThrows
Promise rejects with Error: ER_PARSE_ERROR (MySQL errno 1064)Required handlingWrap await prepare() in try-catch. Validate SQL templates in CI/CD rather than at runtime. Check err.code === 'ER_PARSE_ERROR' to distinguish syntax errors from connection failures.costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisibleSources[5] - prepare · prepare-connection-errorerrorWhenConnection drops between createConnection() and prepare(), or is invalidated by changeUser()Throws
Promise rejects with PROTOCOL_CONNECTION_LOST or ECONNRESETRequired handlingWrap await prepare() in try-catch. Re-prepare statements after any connection reset or changeUser(). Do not cache PreparedStatementInfo objects across connection lifecycle events.costmediumin prodimmediate exceptionusers seeservice unavailablevisibilitysilentSources[5] - PreparedStatementInfo.execute · prepared-statement-constraint-violationerrorWhenBound parameters violate a database constraint (unique, FK, NOT NULL)Throws
Promise rejects with ER_DUP_ENTRY (1062), ER_NO_REFERENCED_ROW_2 (1452), or ER_BAD_NULL_ERROR (1048)Required handlingWrap await stmt.execute() in try-catch. Check err.code to distinguish constraint violations (do not retry without fixing data) from connection errors (may retry).costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisible - PreparedStatementInfo.execute · prepared-statement-stale-after-reconnecterrorWhenPreparedStatementInfo used after connection reset, changeUser(), or reconnectionThrows
Promise rejects with unknown statement ID error or PROTOCOL_CONNECTION_LOSTRequired handlingDo not cache PreparedStatementInfo objects across connection lifecycle events. Re-call connection.prepare() after any changeUser(), reset(), or reconnection. Wrap await stmt.execute() in try-catch.costmediumin prodimmediate exceptionusers seeservice unavailablevisibilitysilent - changeUser · changeuser-access-deniederrorWhenNew user credentials are invalid or not authorized for the target databaseThrows
Promise rejects with ER_ACCESS_DENIED_ERROR (errno 1045); connection marked fatal (err.fatal = true)Required handlingWrap await connection.changeUser() in try-catch. On ER_ACCESS_DENIED_ERROR, destroy the connection and create a new one — the connection is permanently unusable after a fatal changeUser error. Do not return it to the pool.costhighin prodimmediate exceptionusers seeservice unavailablevisibilitysilentSources[8] - changeUser · changeuser-prepared-statements-invalidatedwarningWhenchangeUser() called on a connection that has cached PreparedStatementInfo objectsThrows
Does not throw on changeUser() itself, but subsequent stmt.execute() throws unknown statement ID errorRequired handlingAfter calling changeUser(), invalidate all cached PreparedStatementInfo objects for that connection. Re-call connection.prepare() before using any statement on the connection after changeUser().costmediumin prodimmediate exceptionusers seeservice unavailablevisibilitysilentSources[8] - ping · ping-connection-deaderrorWhenConnection to MySQL is no longer alive (server closed due to wait_timeout or network failure)Throws
Promise rejects with PROTOCOL_CONNECTION_LOST or ECONNRESETRequired handlingWrap await connection.ping() in try-catch. In health check handlers, catch the error and return a 503/unhealthy status rather than letting it propagate as a 500. After a failed ping, destroy the connection and establish a new one.costlowin prodimmediate exceptionusers seeservice unavailablevisibilityvisibleSources[5] - reset · reset-connection-deaderrorWhenThe connection is no longer alive (server closed due to wait_timeout, network failure, or the connection was destroyed) when reset() is called. The COM_RESET_CONNECTION packet cannot be sent on a dead socket.Throws
Promise rejects with PROTOCOL_CONNECTION_LOST, ECONNRESET, or ECONNREFUSED. Same error codes as ping() — the connection is permanently unusable after this error.Required handlingWrap await connection.reset() in try-catch. On error, destroy the connection and create a new one — do not attempt to reuse. In pool scenarios, call connection.destroy() rather than connection.release() when reset() fails. const connection = await pool.getConnection(); try { await connection.reset(); // Use connection... } catch (resetErr) { connection.destroy(); // NOT release() — this connection is broken throw resetErr; } finally { // Only release if no error (destroy() handles cleanup on error) }costmediumin prodimmediate exceptionusers seeservice unavailablevisibilitysilent - reset · reset-invalidates-prepared-statementswarningWhenreset() is called on a connection that has cached PreparedStatementInfo objects (created via connection.prepare()). reset() sends COM_RESET_CONNECTION which causes the MySQL server to invalidate all prepared statements for this connection. The mysql2 client also clears its local _statements LRU cache on reset. Any PreparedStatementInfo objects held by the caller are now stale and will fail when execute() is called on them.Throws
reset() itself succeeds (resolves). Subsequent stmt.execute() on a stale PreparedStatementInfo rejects with unknown statement ID error or PROTOCOL_CONNECTION_LOST.Required handlingAfter calling reset(), invalidate and discard all PreparedStatementInfo objects associated with that connection. Re-call connection.prepare() before executing any statements on the connection. await connection.reset(); // All PreparedStatementInfo from before reset() are now invalid. // Re-prepare any statements you need. const stmt = await connection.prepare('SELECT * FROM users WHERE id = ?'); const [rows] = await stmt.execute([userId]); await stmt.close(); Note: connection.execute() (not prepare()) uses an auto-managed LRU cache that mysql2 clears automatically on reset() — only manually prepared statements via connection.prepare() require manual re-preparation.costmediumin prodimmediate exceptionusers seeservice unavailablevisibilitysilent - PreparedStatementInfo.close · prepared-statement-close-missingwarningWhenconnection.prepare() is called but statement.close() is never called after the prepared statement is no longer needed. Each unclosed prepared statement occupies a slot on the MySQL server. MySQL's global max_prepared_stmt_count (default: 16,382) limits the total number of prepared statements across all connections. Long-running services that repeatedly call connection.prepare() without close() will eventually exhaust this limit.Throws
Once max_prepared_stmt_count is reached, new connection.prepare() calls reject with ER_MAX_PREPARED_STMT_COUNT_REACHED (MySQL errno 1461): "Can't create more than max_prepared_stmt_count statements (current value: 16382)"Required handlingAlways call statement.close() after finishing with a manually-prepared statement. Use try-finally to ensure close() is called even when execute() throws: const stmt = await connection.prepare('INSERT INTO events (type, payload) VALUES (?, ?)'); try { await stmt.execute([eventType, JSON.stringify(payload)]); } finally { await stmt.close(); // Always close — fire-and-forget, always resolves } Prefer connection.execute() (auto-cached LRU, auto-closes evicted statements) over connection.prepare() unless you need explicit statement lifecycle control. The auto-cache manages closing evicted statements transparently. For batch operations where a statement is reused many times, prepare() + close() is more efficient than execute() (which re-parses the statement cache on each call). In these cases, always close in finally.costmediumin proddelayed failureusers seeservice unavailablevisibilitysilent
Sources
Every postcondition cites at least one of these. Numbered to match the footnotes above.
- [1]github.com/sidorares/node-mysql2https://github.com/sidorares/node-mysql2
- [2]dev.mysql.com/doc/mysql-errorshttps://dev.mysql.com/doc/mysql-errors/8.0/en/server-error-reference.html
- [3]github.com/sidorares/node-mysql2https://github.com/sidorares/node-mysql2/blob/master/promise.js
- [4]github.com/sidorares/node-mysql2https://github.com/sidorares/node-mysql2/blob/master/lib/promise/pool.js
- [5]github.com/sidorares/node-mysql2https://github.com/sidorares/node-mysql2/blob/master/lib/promise/connection.js
- [6]github.com/sidorares/node-mysql2https://github.com/sidorares/node-mysql2/blob/master/lib/promise/prepared_statement_info.js
- [7]sidorares.github.io/node-mysql2/docshttps://sidorares.github.io/node-mysql2/docs/documentation/prepared-statements
- [8]github.com/sidorares/node-mysql2https://github.com/sidorares/node-mysql2/blob/master/lib/base/connection.js
- [9]github.com/sidorares/node-mysql2https://github.com/sidorares/node-mysql2/blob/master/lib/commands/reset_connection.js
- [10]github.com/sidorares/node-mysql2https://github.com/sidorares/node-mysql2/blob/master/lib/commands/close_statement.js
- [11]dev.mysql.com/doc/refmanhttps://dev.mysql.com/doc/refman/8.0/en/server-system-variables.html#sysvar_max_prepared_stmt_count
Need a different package?
Request a profile