Skip to content

doc: warn against awaiting replies in module hooks - #65590

Open
bitpshr wants to merge 1 commit into
nodejs:mainfrom
bitpshr:doc/hooks-no-main-thread-await
Open

bitpshr wants to merge 1 commit into
nodejs:mainfrom
bitpshr:doc/hooks-no-main-thread-await

Conversation

@bitpshr

@bitpshr bitpshr commented Aug 27, 2026

Copy link
Copy Markdown
Contributor

Fixes: #63085

module.register() documents that hooks can send messages back to the registering thread, and the example does exactly that. What is not documented is that a hook cannot wait for a reply. Resolution and loading are synchronous from the perspective of the thread that triggered them, so that thread is blocked until the hook settles and can never answer in the meantime. Awaiting a reply inside a resolve or load hook deadlocks both threads.

This adds a short paragraph after the messaging example saying so. The issue reporter hit this and it is not obvious from the surrounding text, which reads as though the channel is bidirectional in the usual way.

Awaiting a reply from the registering thread inside a resolve or load
hook deadlocks: resolution is synchronous from that thread's
perspective, so it is blocked in the request and cannot answer.

Fixes: nodejs#63085
Signed-off-by: Paul Bouchon <mail@bitpshr.net>
@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

Review requested:

  • @nodejs/loaders

@nodejs-github-bot nodejs-github-bot added doc Issues and PRs related to Node.js documentation. module Issues and PRs related to the module subsystem. labels Aug 27, 2026
@bitpshr

bitpshr commented Oct 6, 2026

Copy link
Copy Markdown
Contributor Author

Hi @nodejs/loaders, keeping this on your radar. Doc only: hooks cannot await a reply from the thread that registered them, since that thread is blocked until the hook settles. Green since August.

Comment thread doc/api/module.md
above. It must not wait for a reply while it is running: resolution and loading
are synchronous from the perspective of the thread that triggered them, so that
thread is blocked until the hook settles and cannot answer in the meantime.
Awaiting a reply inside a `resolve` or `load` hook deadlocks both threads.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

That is not true: @nodejs-loaders/tsx effectively does this (it previously did use async/await before supporting both async and sync).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

@JakobJingleheimer I think we are talking about two different things.

I checked @nodejs-loaders/tsx and the awaiting it does is on esbuild.transform(), a promise that resolves on its own:

return transform(rawSource, { sourcefile: url, ...esbuildConfig })
  .catch((f) => handleTrasformErrors(f, url))
  .then(({ code: source, warnings }) => {

What deadlocks is narrower: awaiting a reply from the thread that registered the hook, over the message channel from the preceding example. That thread is blocked inside the resolution or load it triggered, so it cannot run a listener to answer, and both sides wait forever. That is what the reporter hit in #63085.

So hooks can absolutely be async. The only thing that cannot be awaited is a response from the registering thread. My sentence did not make that scope clear, so how about:

A hook can send messages to the thread that registered it at any time, as above. It must not block on a reply from that thread: resolution and loading are synchronous from the perspective of the thread that triggered them, so that thread is blocked until the hook settles and cannot answer in the meantime. Hooks can otherwise be fully asynchronous; it is only a reply over this message channel that cannot be awaited.

Happy to push that, or to drop it entirely if you think the distinction is too narrow to be worth documenting.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Ahh. Yes, much cleaner 🙂 thanks!

@JakobJingleheimer JakobJingleheimer added the author ready PRs with CI started, the required approvals, and no outstanding review comments. label Oct 12, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

author ready PRs with CI started, the required approvals, and no outstanding review comments. doc Issues and PRs related to Node.js documentation. module Issues and PRs related to the module subsystem.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Loader stalls MessagePort since v24

3 participants