Skip to content

fix(ext/napi): restore Explicit microtask policy in napi_resolve_deferred#35212

Merged
bartlomieju merged 1 commit into
mainfrom
fix/napi-resolve-deferred-microtask-policy
Jun 15, 2026
Merged

fix(ext/napi): restore Explicit microtask policy in napi_resolve_deferred#35212
bartlomieju merged 1 commit into
mainfrom
fix/napi-resolve-deferred-microtask-policy

Conversation

@bartlomieju

Copy link
Copy Markdown
Member

napi_resolve_deferred switches the microtask policy to Explicit while
resolving the promise and then restores it afterwards. That restore set
the policy back to Auto, which predated the runtime moving to the
Explicit microtask policy globally (#32466). Because
set_microtasks_policy acts on the whole isolate rather than a scope,
resolving any deferred permanently flipped the entire runtime back to
Auto.

Under the Auto policy V8 auto-drains microtasks whenever the call stack
unwinds to depth zero, including mid-construction inside
napi_new_instance when it runs from native event-loop code such as an
async-work completion. If a drained microtask constructs another
napi-rs class while the factory is still in flight, napi-rs's
class-agnostic factory flag leaks across classes and leaves an unwrapped
instance behind. That is the intermittent "Failed to recover
TsconfigCache type from napi value" failure seen in rolldown/vite
builds. Restoring Explicit instead of Auto keeps the runtime's intended
policy; synchronous JS re-entry from native constructors (for example
zeromq's Object.seal) is unaffected. The regression test recreates the
depth-zero context with async work and asserts the queued microtask does
not drain during construction.

Fixes #33924

…rred

napi_resolve_deferred toggled the microtask policy to Explicit while
resolving and then restored Auto. That restore predated the runtime
switching to the Explicit microtask policy globally (#32466): because
set_microtasks_policy acts on the whole isolate, resolving any deferred
permanently flipped the runtime back to Auto.

Under Auto, V8 auto-drains microtasks whenever the call stack unwinds to
depth zero, including mid-construction inside napi_new_instance when it
runs from native event-loop code. A drained microtask that constructs
another napi-rs class while the factory is still in flight leaves an
unwrapped instance behind, producing the intermittent "Failed to recover
`TsconfigCache` type from napi value" failures (#33924) seen with
rolldown/vite builds.

Restore Explicit instead of Auto so the runtime keeps its intended
policy. Synchronous JS re-entry from native constructors (e.g. zeromq's
Object.seal) is unaffected.
@bartlomieju

Copy link
Copy Markdown
Member Author

Thanks to @motss for the wonderfully clear repro in #33924
(test-deno-vanilla-extract-vite) - it reproduces on a plain debug build
(~33% of runs), which made it straightforward to confirm the root cause
and verify the fix. Much appreciated.

@bartlomieju
bartlomieju merged commit 3346df1 into main Jun 15, 2026
136 checks passed
@bartlomieju
bartlomieju deleted the fix/napi-resolve-deferred-microtask-policy branch June 15, 2026 07:46
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.

Intermittent Failed to recover 'TsconfigCache' type from napi value

1 participant