diff --git a/doc/api/sqlite.md b/doc/api/sqlite.md
index 9e18a12ea90a71..5b1831f40f2076 100644
--- a/doc/api/sqlite.md
+++ b/doc/api/sqlite.md
@@ -234,10 +234,27 @@ added:
* `options` {Object} The configuration options for how the changes will be applied.
* `filter` {Function} Skip changes that, when targeted table name is supplied to this function, return a truthy value.
By default, all changes are attempted.
- * `onConflict` {number} Determines how conflicts are handled. **Default**: `SQLITE_CHANGESET_ABORT`.
- * `SQLITE_CHANGESET_OMIT`: conflicting changes are omitted.
- * `SQLITE_CHANGESET_REPLACE`: conflicting changes replace existing values.
- * `SQLITE_CHANGESET_ABORT`: abort on conflict and roll back database.
+ * `onConflict` {Function} A function that determines how to handle conflicts. The function receives one argument,
+ which can be one of the following values:
+
+ * `SQLITE_CHANGESET_DATA`: A `DELETE` or `UPDATE` change does not contain the expected "before" values.
+ * `SQLITE_CHANGESET_NOTFOUND`: A row matching the primary key of the `DELETE` or `UPDATE` change does not exist.
+ * `SQLITE_CHANGESET_CONFLICT`: An `INSERT` change results in a duplicate primary key.
+ * `SQLITE_CHANGESET_FOREIGN_KEY`: Applying a change would result in a foreign key violation.
+ * `SQLITE_CHANGESET_CONSTRAINT`: Applying a change results in a `UNIQUE`, `CHECK`, or `NOT NULL` constraint
+ violation.
+
+ The function should return one of the following values:
+
+ * `SQLITE_CHANGESET_OMIT`: Omit conflicting changes.
+ * `SQLITE_CHANGESET_REPLACE`: Replace existing values with conflicting changes (only valid with
+ `SQLITE_CHANGESET_DATA` or `SQLITE_CHANGESET_CONFLICT` conflicts).
+ * `SQLITE_CHANGESET_ABORT`: Abort on conflict and roll back the database.
+
+ When an error is thrown in the conflict handler or when any other value is returned from the handler,
+ applying the changeset is aborted and the database is rolled back.
+
+ **Default**: A function that returns `SQLITE_CHANGESET_ABORT`.
* Returns: {boolean} Whether the changeset was applied succesfully without being aborted.
An exception is thrown if the database is not
@@ -496,9 +513,42 @@ An object containing commonly used constants for SQLite operations.
The following constants are exported by the `sqlite.constants` object.
-#### Conflict-resolution constants
+#### Conflict resolution constants
+
+One of the following constants is available as an argument to the `onConflict`
+conflict resolution handler passed to [`database.applyChangeset()`][]. See also
+[Constants Passed To The Conflict Handler][] in the SQLite documentation.
+
+
+
+
Constant
+
Description
+
+
+
SQLITE_CHANGESET_DATA
+
The conflict handler is invoked with this constant when processing a DELETE or UPDATE change if a row with the required PRIMARY KEY fields is present in the database, but one or more other (non primary-key) fields modified by the update do not contain the expected "before" values.
+
+
+
SQLITE_CHANGESET_NOTFOUND
+
The conflict handler is invoked with this constant when processing a DELETE or UPDATE change if a row with the required PRIMARY KEY fields is not present in the database.
+
+
+
SQLITE_CHANGESET_CONFLICT
+
This constant is passed to the conflict handler while processing an INSERT change if the operation would result in duplicate primary key values.
+
+
+
SQLITE_CHANGESET_CONSTRAINT
+
If foreign key handling is enabled, and applying a changeset leaves the database in a state containing foreign key violations, the conflict handler is invoked with this constant exactly once before the changeset is committed. If the conflict handler returns SQLITE_CHANGESET_OMIT, the changes, including those that caused the foreign key constraint violation, are committed. Or, if it returns SQLITE_CHANGESET_ABORT, the changeset is rolled back.
+
+
+
SQLITE_CHANGESET_FOREIGN_KEY
+
If any other constraint violation occurs while applying a change (i.e. a UNIQUE, CHECK or NOT NULL constraint), the conflict handler is invoked with this constant.
+
+
-The following constants are meant for use with [`database.applyChangeset()`](#databaseapplychangesetchangeset-options).
+One of the following constants must be returned from the `onConflict` conflict
+resolution handler passed to [`database.applyChangeset()`][]. See also
+[Constants Returned From The Conflict Handler][] in the SQLite documentation.
@@ -511,7 +561,7 @@ The following constants are meant for use with [`database.applyChangeset()`](#da
SQLITE_CHANGESET_REPLACE
-
Conflicting changes replace existing values.
+
Conflicting changes replace existing values. Note that this value can only be returned when the type of conflict is either SQLITE_CHANGESET_DATA or SQLITE_CHANGESET_CONFLICT.
SQLITE_CHANGESET_ABORT
@@ -520,11 +570,14 @@ The following constants are meant for use with [`database.applyChangeset()`](#da
[Changesets and Patchsets]: https://www.sqlite.org/sessionintro.html#changesets_and_patchsets
+[Constants Passed To The Conflict Handler]: https://www.sqlite.org/session/c_changeset_conflict.html
+[Constants Returned From The Conflict Handler]: https://www.sqlite.org/session/c_changeset_abort.html
[SQL injection]: https://en.wikipedia.org/wiki/SQL_injection
[`ATTACH DATABASE`]: https://www.sqlite.org/lang_attach.html
[`PRAGMA foreign_keys`]: https://www.sqlite.org/pragma.html#pragma_foreign_keys
[`SQLITE_DETERMINISTIC`]: https://www.sqlite.org/c3ref/c_deterministic.html
[`SQLITE_DIRECTONLY`]: https://www.sqlite.org/c3ref/c_deterministic.html
+[`database.applyChangeset()`]: #databaseapplychangesetchangeset-options
[`sqlite3_changes64()`]: https://www.sqlite.org/c3ref/changes.html
[`sqlite3_close_v2()`]: https://www.sqlite.org/c3ref/close.html
[`sqlite3_create_function_v2()`]: https://www.sqlite.org/c3ref/create_function.html
diff --git a/src/env_properties.h b/src/env_properties.h
index 592e95b0584a87..d9e18cbc4515ac 100644
--- a/src/env_properties.h
+++ b/src/env_properties.h
@@ -144,10 +144,13 @@
V(entry_type_string, "entryType") \
V(env_pairs_string, "envPairs") \
V(env_var_settings_string, "envVarSettings") \
+ V(err_sqlite_error_string, "ERR_SQLITE_ERROR") \
+ V(errcode_string, "errcode") \
V(errno_string, "errno") \
V(error_string, "error") \
- V(events, "events") \
+ V(errstr_string, "errstr") \
V(events_waiting, "eventsWaiting") \
+ V(events, "events") \
V(exchange_string, "exchange") \
V(expire_string, "expire") \
V(exponent_string, "exponent") \
diff --git a/src/node_sqlite.cc b/src/node_sqlite.cc
index 7f5e2f89ce9dba..abd85a98c5aebb 100644
--- a/src/node_sqlite.cc
+++ b/src/node_sqlite.cc
@@ -42,6 +42,7 @@ using v8::Number;
using v8::Object;
using v8::SideEffectType;
using v8::String;
+using v8::TryCatch;
using v8::Uint8Array;
using v8::Value;
@@ -66,13 +67,14 @@ inline MaybeLocal