slint-ui/slint · error · std::io::Error

{x:?}

Error message

{x:?}

What it means

When the LSP server compiled to WASM asks the injected JavaScript `load_file` callback to fetch a file's contents, any JS-side rejection of that call is converted into a std::io::Error whose message is the Debug formatting of the wasm_bindgen JsValue. The library throws it because it cannot proceed loading the requested URI without the file content the host JS environment failed to provide. The message is opaque (`JsValue(...)` debug output) because the Rust side cannot know the JS error's structure.

Solutions

  1. Inspect the browser console/network tab to find the real JS error behind the debug-formatted JsValue (404, CORS, network).
  2. Verify the URI passed by the LSP client maps to a URL your `load_file` callback can fetch from the web page origin.
  3. Fix the host JS `load_file` implementation to return a Promise resolving to the file's UTF-8 string and to propagate readable error messages.
  4. Serve the WASM bundle and files from the same origin or configure CORS headers on the file server.
  5. Retry the request after fixing connectivity; transient network failures also surface here.

Example fix

// before (host JS, throws opaque rejection)
const load_file = (path) => fetch(path);
// after
const load_file = async (path) => {
  const res = await fetch(path);
  if (!res.ok) throw new Error(`failed to load ${path}: HTTP ${res.status}`);
  return res.text();
};
Defensive patterns

Strategy: try-catch

Validate before calling

// JS host side, before registering the callback
async function validateLoadFile(path) {
  const res = await fetch(path, { method: 'HEAD' });
  if (!res.ok) throw new Error(`unreachable: ${path} -> ${res.status}`);
}

Type guard

// JS host side
function isPromise(v) { return v && typeof v.then === 'function'; }

Try / catch

// JS host side wrapping the callback given to the WASM LSP
const load_file = async (path) => {
  try {
    const res = await fetch(path);
    if (!res.ok) throw new Error(`HTTP ${res.status}`);
    return await res.text();
  } catch (err) {
    console.error('load_file failed for', path, err); // read this, not the Rust Debug output
    throw err;
  }
};

Prevention

When it happens

Trigger: Calling LSP operations that need file contents (e.g. opening a document, diagnostics) when the injected `load_file` JS function rejects: the fetch/XHR in the browser fails, the JS callback throws, or it returns a non-Promise value.

Common situations: Serving the WASM LSP from a page where the resource path is wrong (404), CORS blocks the fetch, the file server is down, or the host page supplies a broken/misbehaving `load_file` callback (e.g. returns a rejected Promise or undefined).

Understand the failure class

Background: "failed to read file", EACCES, ENOENT and "could not read <path>" errors: when a program can't read a file from disk — this error's family across 49 libraries.

Related errors


AI-assisted analysis of slint-ui/slint@bb937076de (2026-09-16). Data as JSON: /api/errors/df3f4b9f86d40caa. Report an issue: GitHub.

Appendix: source

Thrown at tools/lsp/wasm_main.rs:477

        let mut ctx = self.ctx.lock().await;
        let result = self
            .rh
            .handle_request(method, params, &mut ctx)
            .map_err(|e| JsError::new(&e.to_string()))?;
        Ok(result)
    }

    #[wasm_bindgen]
    pub async fn reload_config(&self) -> JsResult<()> {
        let mut ctx = self.ctx.lock().await;
        language::load_configuration(&mut ctx).await.map_err(|e| JsError::new(&e.to_string()))
    }
}

async fn load_file(path: String, load_file: &Function) -> std::io::Result<String> {
    let string_promise = load_file
        .call1(&JsValue::UNDEFINED, &path.into())
        .map_err(|x| std::io::Error::new(ErrorKind::Other, format!("{x:?}")))?;
    let string_future = wasm_bindgen_futures::JsFuture::from(js_sys::Promise::from(string_promise));
    let js_value =
        string_future.await.map_err(|e| std::io::Error::new(ErrorKind::Other, format!("{e:?}")))?;
    return Ok(js_value.as_string().unwrap_or_default());
}

// Use a JSON friendly representation to avoid using ES maps instead of JS objects.
fn to_value<T: serde::Serialize + ?Sized>(
    value: &T,
) -> std::result::Result<wasm_bindgen::JsValue, serde_wasm_bindgen::Error> {
    value.serialize(&serde_wasm_bindgen::Serializer::json_compatible())
}

View on GitHub (pinned to bb937076de)