Skip to content

Throw the named error type from JSError.Throw*Error(string) - #511

Open
René Larch (r-Larch) wants to merge 2 commits into
microsoft:mainfrom
r-Larch:fix/typed-throw-errors
Open

René Larch (r-Larch) wants to merge 2 commits into
microsoft:mainfrom
r-Larch:fix/typed-throw-errors

Conversation

@r-Larch

@r-Larch René Larch (r-Larch) commented Oct 21, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #509.

Stacked on #510. This branch includes #510's commit, because the test passes code and msg through the same unterminated buffers (without #510, code came back as ERR_TESTor). Only the last commit, "Throw the named error type from JSError.Throw*Error(string)", belongs to this PR. I'll rebase once #510 is merged.

JSError.ThrowTypeError, ThrowRangeError and ThrowSyntaxError (with and without code) all called JSRuntime.ThrowError, so JS received a plain Error. They now call the matching runtime method (napi_throw_type_error, napi_throw_range_error, node_api_throw_syntax_error).

Tests

NodejsEmbeddingTests.ThrowTypedError checks name, instanceof, message and code for Error, TypeError, RangeError and SyntaxError, with and without a code. The non-Error cases fail on main.

Local run (Windows x64, .NET SDK 10.0.402): net9.0 134/135 and net8.0 134/135. On net10.0 everything passed except napi-dotnet/overloads (hosted and AOT), which fails the same way on main. I could not run net472 locally.

🤖 Generated with Claude Code

Copilot AI balanced review requested due to automatic review settings October 21, 2026 20:14
@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
There may be pipelines that require an authorized user to comment /azp run to run.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Approval recommended

The focused fixes match the reported defects and include targeted regression coverage, with no unresolved findings.

No open issues 🎉

What changed in this PR

Fixes #509 by making typed JSError helpers throw the matching JavaScript error type. Includes the UTF-8 buffer fixes stacked from #510.

Changes:

  • Routes typed throw helpers to their matching runtime methods.
  • Explicitly terminates UTF-8 strings and disposes the module-name buffer.
  • Adds regression tests for error types and buffer handling.
File Description
test/​Utf8BufferTests.cs Tests UTF-8 termination and null handling.
test/​TestUtils.cs Adds a helper to dirty pooled buffers.
test/​NodejsEmbeddingTests.cs Tests thrown error types, messages, and codes.
src/​NodeApi/​Runtime/​Utf8StringArray.cs Writes each string’s terminator explicitly.
src/​NodeApi/​Runtime/​PooledBuffer.cs Terminates UTF-8 buffers and distinguishes empty from null.
src/​NodeApi/​Runtime/​NodejsRuntime.Embedding.cs Disposes the module-name buffer.
src/​NodeApi/​JSError.cs Calls the matching typed runtime throw methods.

Review effort: Balanced

@r-Larch

Copy link
Copy Markdown
Contributor Author

@microsoft-github-policy-service agree

René Larch (r-Larch) and others added 2 commits October 29, 2026 03:43
PooledBuffer.FromStringUtf8 and FromSpanUtf8 reserved a byte for the
terminator but never wrote it. On .NET the array comes from
ArrayPool<byte>.Shared, which is not zeroed, so native functions that take a
null-terminated const char* without a length read stale bytes past the string
(or past the array): napi_throw_error / napi_throw_type_error /
napi_throw_range_error / node_api_throw_syntax_error (code and msg),
node_embedding_runtime_config_add_module and
node_embedding_last_error_message_set. For example JSError.ThrowError("x")
produced a JS error message of "x" followed by garbage, and a linked module
could be registered under a corrupted name.

- Write the terminator explicitly in both helpers.
- Encode an empty string as just the terminator instead of returning
  PooledBuffer.Empty, which pinned to a null pointer (ThrowError("") passed
  msg = NULL, so napi_throw_error failed and nothing was thrown). A null
  string still maps to a null pointer.
- Return the module-name buffer to the pool in
  EmbeddingRuntimeConfigAddModule (Node.js copies the name).

Fixes microsoft#508.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
JSError.ThrowTypeError, ThrowRangeError and ThrowSyntaxError (with and
without a code) all called JSRuntime.ThrowError, so JS received a plain Error
instead of a TypeError / RangeError / SyntaxError. Call the matching runtime
method instead.

Fixes microsoft#509.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Approval recommended

No active review issues are reported for the typed-error dispatch and UTF-8 buffer changes.

No open issues 🎉

Review effort: Balanced

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

JSError.ThrowTypeError / ThrowRangeError / ThrowSyntaxError throw a plain Error

2 participants