From 9f74b58d66d1fcf59cdf82937dcb1d1aa546466a Mon Sep 17 00:00:00 2001 From: ybai08 <271969568+ybai08@users.noreply.github.com> Date: Sun, 18 Oct 2026 17:45:50 -0500 Subject: [PATCH] fix: report the message of Errors thrown in user-defined functions When a function registered with create_function, or the step or finalize function of an aggregate, threw an Error object, the error was passed as-is to sqlite3_result_error, which is wrapped to take a string. The Error was converted to an empty string, so the query failed with an empty message, and debug builds aborted with "stringToUTF8Array expects a string (got object)". Only thrown strings were reported correctly. Pass the message of Error objects, and the string conversion of any other thrown value, to sqlite3_result_error. --- src/api.js | 12 +++++++++--- test/test_aggregate_functions.js | 19 +++++++++++++++++++ test/test_functions.js | 6 +++++- 3 files changed, 33 insertions(+), 4 deletions(-) diff --git a/src/api.js b/src/api.js index 47d64ef1..e391305c 100644 --- a/src/api.js +++ b/src/api.js @@ -1245,6 +1245,12 @@ Module["onRuntimeInitialized"] = function onRuntimeInitialized() { sqlite3_result_null(cx); } }; + var setFunctionError = function setFunctionError(cx, error) { + // sqlite3_result_error takes a string: passing an Error object + // directly would report an empty message + var message = error instanceof Error ? error.message : String(error); + sqlite3_result_error(cx, message, -1); + }; /** Register a custom function with SQLite @example Register a simple function @@ -1266,7 +1272,7 @@ Module["onRuntimeInitialized"] = function onRuntimeInitialized() { try { result = func.apply(null, args); } catch (error) { - sqlite3_result_error(cx, error, -1); + setFunctionError(cx, error); return; } setFunctionResult(cx, result); @@ -1367,7 +1373,7 @@ Module["onRuntimeInitialized"] = function onRuntimeInitialized() { state[p] = step.apply(null, mergedArgs); } catch (error) { delete state[p]; - sqlite3_result_error(cx, error, -1); + setFunctionError(cx, error); } } @@ -1378,7 +1384,7 @@ Module["onRuntimeInitialized"] = function onRuntimeInitialized() { result = finalize(state[p]); } catch (error) { delete state[p]; - sqlite3_result_error(cx, error, -1); + setFunctionError(cx, error); return; } setFunctionResult(cx, result); diff --git a/test/test_aggregate_functions.js b/test/test_aggregate_functions.js index d28c775f..bcbbdb9d 100644 --- a/test/test_aggregate_functions.js +++ b/test/test_aggregate_functions.js @@ -99,6 +99,25 @@ exports.test = function (SQL, assert) { 3, "Aggregate functions work after an exception has been thrown in finalize" ); + + db.create_aggregate("throws_error_step", { + step: (state, value) => { throw new Error("step error object"); }, + }); + assert.throws( + () => db.exec("SELECT throws_error_step(column1) FROM (VALUES (1));"), + /step error object/, + "Reports the message of an Error thrown in a step function" + ); + + db.create_aggregate("throws_error_finalize", { + step: (state, value) => value, + finalize: (state) => { throw new Error("finalize error object"); }, + }); + assert.throws( + () => db.exec("SELECT throws_error_finalize(column1) FROM (VALUES (1));"), + /finalize error object/, + "Reports the message of an Error thrown in a finalize function" + ); } // helper function to calculate a percentile from an array. Will modify the diff --git a/test/test_functions.js b/test/test_functions.js index eb1cc2a8..c0b17193 100644 --- a/test/test_functions.js +++ b/test/test_functions.js @@ -149,7 +149,11 @@ exports.test = function(SQL, assert){ db.create_function("throwFunction", function () { throw "internal exception"; return 5;} ); assert.throws(function(){db.exec("SELECT throwFunction()");},/internal exception/, "Can handle internal exceptions"); - + + db.create_function("throwErrorFunction", function () { throw new Error("internal error object"); }); + assert.throws(function(){db.exec("SELECT throwErrorFunction()");},/internal error object/, "Reports the message of a thrown Error"); + assert.equal(db.exec("SELECT addOne(1);")[0]["values"][0][0], 2, "Functions work after an Error has been thrown"); + db.create_function("customeObjectFunction", function () { return {test:123};} ); assert.throws(function(){db.exec("SELECT customeObjectFunction()");},/Wrong API use/, "Reports wrong API use");