The N-API implementation was not properly constructing JavaScript Error objects for ODBC errors. Instead of receiving proper Error instances with ODBC-specific properties, JavaScript callbacks were receiving plain objects with only a message property.
Expected behavior (from original driver):
err instanceof Error === true
err.sqlstate === "42000"
err.code === 105
err.severity === 15
// etc.Actual behavior (broken):
err = { "message": "..." } // Plain object, not an Error- ODBC Error Detection: C++ correctly detected ODBC errors and populated error details
- Error Details Missing: The
QueryWorkerwasn't capturing full ODBC error information inerrorDetails_ - Wrong Object Type: The
OnErrormethod was creating plain JavaScript objects instead of Error objects
File: cpp/src/js/workers/query_worker.cpp
Problem: Only extracting error message, discarding other ODBC details
// Before:
const std::string errorMessage = errors[0]->message;
SetError(errorMessage);Fix: Populate errorDetails_ with all ODBC error information
// After:
// Populate errorDetails_ with all ODBC errors
errorDetails_ = errors;
const std::string errorMessage = errors[0]->message;
SetError(errorMessage);File: cpp/src/js/workers/odbc_async_worker.cpp
Problem: Creating plain JavaScript objects instead of Error objects
// Before:
Napi::Object errorObj = Napi::Object::New(env);
errorObj.Set("message", error.Message());
// ... set properties on plain object
Callback().Call({errorObj, env.Null()});Fix: Create actual JavaScript Error objects with ODBC properties
// After:
Napi::Error jsError = Napi::Error::New(env, error.Message());
// Add ODBC-specific properties to the Error object
jsError.Set("sqlstate", Napi::String::New(env, firstError->sqlstate));
jsError.Set("code", Napi::Number::New(env, firstError->code));
jsError.Set("severity", Napi::Number::New(env, firstError->severity));
jsError.Set("serverName", Napi::String::New(env, firstError->serverName));
jsError.Set("procName", Napi::String::New(env, firstError->procName));
jsError.Set("lineNumber", Napi::Number::New(env, firstError->lineNumber));
// ... add details array
Callback().Call({jsError.Value(), env.Null()});// Original test expectation (now passing):
assert(e instanceof Error) // ✅ true
assert.strictEqual(e.sqlstate, '42000') // ✅ "42000"
assert.strictEqual(e.code, 105) // ✅ 105
assert.strictEqual(e.severity, 15) // ✅ 15
assert.strictEqual(e.procName, '') // ✅ ""
assert.strictEqual(e.lineNumber, 1) // ✅ 1
assert(e.serverName.length > 0) // ✅ populated{
message: "[Microsoft][ODBC Driver 18 for SQL Server][SQL Server]Unclosed quotation mark...",
sqlstate: "42000",
code: 105,
severity: 15,
serverName: "4353696fa8ed",
procName: "",
lineNumber: 1,
details: [
{
sqlstate: "42000",
message: "...",
code: 105,
severity: 15,
serverName: "4353696fa8ed",
procName: "",
lineNumber: 1
}
]
}- Backward Compatibility: Restored compatibility with existing error handling code
- API Consistency: Error objects now match the original driver's behavior
- Debugging: Applications can now access ODBC-specific error details
- Type Safety:
instanceof Errorchecks work correctly
cpp/src/js/workers/query_worker.cpp- Capture full error detailscpp/src/js/workers/odbc_async_worker.cpp- Create proper Error objects
JavaScript applications can now handle ODBC errors as expected:
connection.queryRaw("INVALID SQL", (err, results) => {
if (err) {
console.log(err instanceof Error); // true
console.log('SQL State:', err.sqlstate); // "42000"
console.log('Error Code:', err.code); // specific error number
console.log('Severity:', err.severity); // error severity level
console.log('Server:', err.serverName); // SQL Server instance
console.log('Line:', err.lineNumber); // line number in SQL
}
});