BoundaryML/baml · error · anyhow::Error

readFileRef did not return a promise

Error message

readFileRef did not return a promise

What it means

The readFileRef callback injected from JS (get_baml_src_cb) was expected to return a Promise for the given adjusted path, but calling it returned null/undefined or threw synchronously. The Rust WASM runtime bails so it cannot read the BAML source file asynchronously.

Solutions

  1. Ensure the readFileRef option is an async function returning a Promise<ArrayBuffer|Uint8Array>
  2. Confirm readFileRef is registered/defined before BAML runtime initialization
  3. Validate the path passed to readFileRef resolves in the host environment
  4. Check that the callback doesn't throw synchronously before returning the promise

Example fix

// before
b.init({ readFileRef: (path) => fs.readFileSync(path) })
// after
b.init({ readFileRef: async (path) => fs.promises.readFile(path) })
Defensive patterns

Strategy: validation

Validate before calling

// validate readFileRef before initializing BAML
if (typeof opts.readFileRef !== 'function') throw new Error('readFileRef must be a function');
const probe = opts.readFileRef('probe.baml');
if (!(probe instanceof Promise)) throw new Error('readFileRef must return a Promise (make it async)');

Type guard

const isReadFileRef = (f) => typeof f === 'function' && f('x') instanceof Promise;

Try / catch

try {
  await b.init(opts);
} catch (e) {
  if (String(e).includes('readFileRef did not return a promise')) {
    console.error('readFileRef must be async and return a Promise');
  }
  throw e;
}

Prevention

When it happens

Trigger: invoke get_baml_src_cb.call1(...) in the wasm runtime when the JS host's readFileRef is not a function returning a Promise, is undefined, or throws before returning (e.g. invalid path rejected synchronously).

Common situations: Using BAML in a JS/TS environment (Next.js, Vite, browser) where the readFileRef option is misconfigured — passed a sync function, an async wrapper mismatch, or omitted so the callback misbehaves.

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 BoundaryML/baml@bd85ce9dee (2026-09-12). Data as JSON: /api/errors/a56c02a3d447e814. Report an issue: GitHub.

Appendix: source

Thrown at engine/baml-schema-wasm/src/runtime_wasm/mod.rs:2375

                    if is_windows && (path.starts_with("../") || path.starts_with("./")) {
                        let result = format!("baml_src/{path}");
                        web_sys::console::log_1(&wasm_bindgen::JsValue::from_str(&format!(
                            "WASM Windows path fix applied: '{path}' → '{result}'"
                        )));
                        result
                    } else {
                        web_sys::console::log_1(&wasm_bindgen::JsValue::from_str(&format!(
                            "WASM path unchanged: '{}' (windows={}, relative={})",
                            path,
                            is_windows,
                            path.starts_with("../") || path.starts_with("./")
                        )));
                        path.clone()
                    };

                let null = JsValue::NULL;
                let Ok(read) = get_baml_src_cb.call1(&null, &JsValue::from(adjusted_path)) else {
                    anyhow::bail!("readFileRef did not return a promise");
                };

                let read = JsFuture::from(Promise::unchecked_from_js(read)).await;

                let read = match read {
                    Ok(read) => read,
                    Err(err) => {
                        if let Some(e) = err.dyn_ref::<js_sys::Error>() {
                            if let Some(e_str) = e.message().as_string() {
                                anyhow::bail!("readFileRef failure: {}", e_str);
                            }
                        }

                        anyhow::bail!("readFileRef rejected: {:?}", err);
                    }
                };

                // TODO: how does JsValue -> Uint8Array work without try_from?

View on GitHub (pinned to bd85ce9dee)