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
- Inspect the browser console/network tab to find the real JS error behind the debug-formatted JsValue (404, CORS, network).
- Verify the URI passed by the LSP client maps to a URL your `load_file` callback can fetch from the web page origin.
- 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.
- Serve the WASM bundle and files from the same origin or configure CORS headers on the file server.
- 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
- Serve files same-origin or configure CORS before using the WASM LSP.
- Always return a Promise<string> from the load_file callback.
- Log JS-side errors verbosely; the Rust error only shows Debug(JsValue).
- Test LSP startup against the real served paths, including 404 cases.
- Monitor the network tab during LSP operation in dev tools.
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)