Skip to content

[Panic]: deferred_scan_data.rs:45 - "Should have resolved id: NotFound" #8236

@steffanhalv

Description

@steffanhalv

Panic message

# Rolldown Panic Report: `deferred_scan_data.rs:45` - "Should have resolved id: NotFound"

`@rolldown/browser@1.0.0-rc.2` panics during the Vite build process with:


thread 'tokio-runtime-worker' (2) panicked at
  /home/runner/work/rolldown/rolldown/crates/rolldown/src/module_loader/deferred_scan_data.rs:45:12:
Should have resolved id: NotFound("/ecommerce/src/entry-client.js")


The panic occurs in the `defer_sync_scan_data` function when `resolver.resolve()` returns `NotFound` for a module that was **successfully resolved and scanned** during the initial scan phase. The `.expect("Should have resolved id")` on line 45 crashes instead of handling the error gracefully.


### Full console output (in order)


wasi-worker-browser.mjs:19  Rolldown panicked. This is a bug in Rolldown, not your code.

wasi-worker-browser.mjs:19  thread 'tokio-runtime-worker' (2) panicked at
  /home/runner/work/rolldown/rolldown/crates/rolldown/src/module_loader/deferred_scan_data.rs:45:12:

wasi-worker-browser.mjs:19  Should have resolved id: NotFound("/ecommerce/src/entry-client.js")

wasi-worker-browser.mjs:19  note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

wasi-worker-browser.mjs:19  Please report this issue at:
  https://github.com/rolldown/rolldown/issues/new?template=panic_report.yml

wasi-threads.esm-bundler.js:257  worker (tid = 44) sent an error!
  Uncaught RuntimeError: unreachable


### WASM stack trace


$wasi_thread_start @ 02a212da:1
ThreadMessageHandler._start @ wasi-threads.esm-bundler.js:890
ThreadMessageHandler.handleAfterLoad @ wasi-threads.esm-bundler.js:930
ThreadMessageHandler.handle @ wasi-threads.esm-bundler.js:847
MessageHandler.handle @ emnapi-core.esm-bundler.js:7609
globalThis.onmessage @ wasi-worker-browser.mjs:38


## Root Cause Analysis

Looking at [`deferred_scan_data.rs`](https://github.com/rolldown/rolldown/blob/main/crates/rolldown/src/module_loader/deferred_scan_data.rs), line 45:


let resolved_id = resolver
    .resolve(None, source_id.as_str(), ImportKind::Import, normal.is_user_defined_entry)
    .expect("Should have resolved id")  // <-- LINE 45: panics here
    .into();

Reproduction

  1. Use @rolldown/browser (WASM build) in a browser Web Worker environment with a custom WASI filesystem (VFS backed by OPFS + SharedArrayBuffer sync proxy)
  2. Run a Vite build (vite build) on a standard Vite + React + Tailwind CSS project
  3. Vite calls rolldown.rollup(options) then bundle.generate()
  4. The initial scan phase completes successfully — all modules are resolved and scanned (2600+ WASI filesystem operations succeed)
  5. During the bundling phase, defer_sync_scan_data runs and calls resolver.resolve(None, "/ecommerce/src/entry-client.js", ImportKind::Import, true)
  6. The resolver returns NotFound even though the file exists and was successfully resolved during the initial scan
  7. .expect("Should have resolved id") panics

System Info

## Environment

- **Package**: `@rolldown/browser@1.0.0-rc.2`
- **Runtime**: Browser (Chrome 133), running in a Web Worker with SharedArrayBuffer + WASI threads
- **Build tool**: Vite (using `@rolldown/browser` as the bundler via WASM)
- **Filesystem**: Custom WASI filesystem (VFS backed by OPFS + SharedArrayBuffer sync proxy)
- **Project**: Standard Vite + React + Tailwind CSS ecommerce template
- **Reproducibility**: 100% — happens on every build attempt

Additional context

The issue:

  1. During the initial scan phase, /ecommerce/src/entry-client.js is successfully resolved and scanned (confirmed by 2600+ successful WASI filesystem operations including statSync on this exact file).
  2. During defer_sync_scan_data, the resolver tries to re-resolve the same file path with importer: None.
  3. This second resolution returns NotFound, even though the file exists and was previously found.
  4. The .expect() panics instead of handling the error.

The resolver.resolve(None, source_id, ...) call passes None as the importer (first argument), which changes the resolution context compared to the original scan (where the importer was the parent module). The comment on lines 42-43 acknowledges this: "other params except source_id is not important, since we need package_json" — but this assumption can break when the importer context matters.

In our WASI filesystem environment, resolving an absolute path /ecommerce/src/entry-client.js with no importer context fails, even though the file physically exists.

Evidence the File Exists

The build logs show the file being successfully accessed multiple times before the panic:

[WASI-fs #581]  statSync /ecommerce/src/entry-client.js {bigint}     ✓
[WASI-fs #685]  statSync /ecommerce/src/entry-client.js {bigint}     ✓
[WASI-fs #695]  statSync /ecommerce/src/entry-client.js {bigint}     ✓
[WASI-fs #696]  lstatSync /ecommerce/src/entry-client.js             ✓
[WASI-fs #971]  statSync /ecommerce/src/entry-client.js {bigint}     ✓
[WASI-fs #1075] statSync /ecommerce/src/entry-client.js {bigint}     ✓
[WASI-fs #1085] statSync /ecommerce/src/entry-client.js {bigint}     ✓
[WASI-fs #1086] lstatSync /ecommerce/src/entry-client.js             ✓
[WASI-fs #1818] statSync /ecommerce/src/entry-client.js {bigint}     ✓

All statSync calls returned successfully (no ENOENT). The file is a real .js file on the VFS, not a virtual module.

Suggested Fix

Replace the .expect() with graceful error handling:

// Before (panics):
let resolved_id = resolver
    .resolve(None, source_id.as_str(), ImportKind::Import, normal.is_user_defined_entry)
    .expect("Should have resolved id")
    .into();

// After (graceful):
let Ok(resolved) = resolver
    .resolve(None, source_id.as_str(), ImportKind::Import, normal.is_user_defined_entry)
else {
    // If re-resolution fails, skip side-effect analysis for this module
    continue;
};
let resolved_id = resolved.into();

This matches the pattern used elsewhere in the same function (lines 24-31) where module_id_to_idx.get(), module_table.modules.get_mut(), and module.as_normal_mut() all use let Some(...) else { continue } for graceful fallback.

Related Issues

  • #6569 — "should have idx" panic in the same defer_sync_scan_data code path (fixed, but the .expect() on line 45 was left intact)
  • #898 — WASI support tracking issue (documents filesystem and resolver limitations in WASM builds)

Build Context

The Vite build successfully completes:

  • Config loading and bundling
  • Vite initialization with rolldown
  • Tailwind CSS oxide scanner (1200+ WASI filesystem calls)
  • Rayon worker thread creation and scanning
  • Module resolution for all project files

Then panics during the bundling phase in defer_sync_scan_data. The bundle.generate() call never returns, causing the build to hang indefinitely.

Metadata

Metadata

Assignees

Type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions