libnyanpasu/clash-nyanpasu · critical

invalid url

Error message

invalid url

What it means

In the Boa JS runtime's HTTP module loader, `load_imported_module` parses the import specifier with `Url::from_str(...).expect("invalid url")`. Because `.expect` panics rather than returning a JsResult, an import specifier that is not an absolute, valid URL aborts the thread with this message.

Source

Thrown at backend/boa_utils/src/module/http.rs:94

            }
        };

        // Could also add a path if needed.
        let source = Source::from_bytes(source_str.as_bytes());

        Module::parse(source, None, context)
    }
}

impl ModuleLoader for HttpModuleLoader {
    async fn load_imported_module(
        self: Rc<Self>,
        _referrer: boa_engine::module::Referrer,
        request: ModuleRequest,
        context: &RefCell<&mut Context>,
    ) -> JsResult<Module> {
        let url = request.specifier().to_std_string_escaped();
        let url = Url::from_str(&url).expect("invalid url"); // SAFETY: `url` is a valid URL, if it's not, its caller side issue
        let cache_path = self.mapping_cache_dir(&url);
        let parent_dir = cache_path
            .parent()
            .ok_or_else(|| {
                log::error!("failed to get parent directory for `{url}`");
                JsNativeError::typ().with_message(format!(
                    "failed to get cache parent directory for `{url}`; path: `{}`",
                    cache_path.display()
                ))
            })?
            .to_path_buf();

        let max_age = self.max_age;

        log::debug!("checking cache for `{url}`...");

        let now = SystemTime::now();
        let should_use_cached_content = match async_fs::metadata(&cache_path).await {

View on GitHub (pinned to f7dbce2997)

Solutions

  1. Use fully-qualified absolute URLs in import statements (e.g. `https://example.com/mod.js`).
  2. Change the loader to return a JsNativeError instead of `.expect` to convert the panic into a catchable JS error.
  3. Pre-register local modules in the module map so imports resolve without URL fetching.

Example fix

// before
let url = Url::from_str(&url).expect("invalid url");
// after
let url = Url::from_str(&url).map_err(|_| JsNativeError::typ().with_message(format!("invalid url: {url}")))?;
Defensive patterns

Strategy: validation

Validate before calling

function assertAbsoluteUrl(specifier) {
  const u = new URL(specifier);
  if (!(u.protocol === 'https:' || u.protocol === 'http:')) {
    throw new Error(`import specifier must be absolute http(s) URL: ${specifier}`);
  }
  return u.href;
}
// call before dynamic import: import(assertAbsoluteUrl(spec))

Type guard

function isAbsoluteHttpUrl(s) {
  try { const u = new URL(s); return u.protocol === 'http:' || u.protocol === 'https:'; }
  catch { return false; }
}

Try / catch

try {
  await import(spec);
} catch (e) {
  // loader panics on non-URL specifiers; validate first, never pass relative paths
  console.error('module load failed (use absolute URLs):', spec);
}

Prevention

When it happens

Trigger: A JS module executes `import ... from "./relative/path"` or any bare/non-URL specifier; `Url::from_str` fails and the expect panics inside `load_imported_module`.

Common situations: Scripts using relative imports (the loader only supports absolute http(s) URLs), typos in specifiers, or bundler-style bare imports like `import x from 'lodash'`.

Understand the failure class

Background: "Invalid URL" errors: why new URL(), URI.parse, and reqwest::Url reject your string — missing scheme, whitespace, and bad path format — this error's family across 39 libraries.

Related errors


AI-assisted analysis of libnyanpasu/clash-nyanpasu@f7dbce2997 (2026-09-08). Data as JSON: /api/errors/709cb5a863576fd4. Report an issue: GitHub.