facebook/flow · error · Error

flow.js wasm is still loading; await flow.ready first.

Error message

flow.js wasm is still loading; await flow.ready first.

What it means

Every flow.js API call goes through callRust -> initWasm(). The WASM module is instantiated asynchronously: loadWasm() starts the load and stores the promise in flowDotJsReady, which is exported as flow.ready (src/flow_dot_js_wasm.js:345). initWasm() throws this error when loadWasm() has been kicked off but flowDotJsWasmModule is still null, i.e. the Emscripten module has not finished instantiating yet.

Solutions

  1. Await flow.ready once before the first API call (same pattern as src/__tests__/flow_dot_js_smoke_test.js: await flow.ready)
  2. Gate every flow entry point behind a shared async init wrapper so callers cannot bypass the await
  3. If using the modular factory form, make sure flow_dot_js_wasm() is defined and resolves before calls (the promise path installs exports in installWasmModule)
  4. In browsers, verify the .wasm binary is fetched successfully (a failed fetch keeps the module null and this error rethrows on later calls)

Example fix

// before
const flow = require('flow.js');
const errors = flow.checkContent(a, b, 'foo.js', 'const x: number = "s";'); // throws: wasm is still loading

// after
const flow = require('flow.js');
(async () => {
  await flow.ready;
  const errors = flow.checkContent(a, b, 'foo.js', 'const x: number = "s";');
})();
Defensive patterns

Strategy: validation

Validate before calling

// run before any flow.js API call
await flow.ready; // resolves once installWasmModule has installed the exports

Type guard

// after installWasmModule runs it sets flowVersion from a live callRust(),
// so a string flowVersion means the wasm module is installed.
function isFlowJsReady(flow) {
  return typeof flow.flowVersion === 'string';
}

Try / catch

try {
  flow.checkContent(a, b, file, code);
} catch (e) {
  if (e instanceof Error && /still loading/.test(e.message)) {
    await flow.ready;
    return flow.checkContent(a, b, file, code); // retry exactly once
  }
  throw e;
}

Prevention

When it happens

Trigger: Calling any exported flow.js method (checkContent, parse, completions, flowVersion after install, etc.) synchronously right after require/import, before the ready promise resolves. Calling flow from module top-level code or a non-async callback that races the WASM instantiation.

Common situations: Editor/IDE integrations and LSP servers that query flow during startup; test harnesses that invoke flow without awaiting ready; slower environments (big WASM binary, cold cache, CI) where instantiation takes longer than the caller assumed; refactors that moved the first flow call outside the async init path.

Related errors


AI-assisted analysis of facebook/flow@f88ac94bcf (2026-08-20). Data as JSON: /api/errors/fed679af756547e1. Report an issue: GitHub.

Appendix: source

Thrown at src/flow_dot_js_wasm.js:212

        Module.onRuntimeInitialized = function () {
          if (typeof onRuntimeInitialized === 'function') {
            onRuntimeInitialized();
          }
          finish();
        };
      }
    });
  }
  return flowDotJsReady;
}

function initWasm() {
  if (flowDotJsWasmModule != null) {
    return;
  }
  loadWasm();
  if (flowDotJsWasmModule == null) {
    throw new Error('flow.js wasm is still loading; await flow.ready first.');
  }
}

function callRust(method, params) {
  initWasm();
  const request = encodeUtf8(JSON.stringify({method, params}));
  const requestPtr = flowDotJsAlloc(request.length);
  updateFlowDotJsMemoryViews();
  let responsePtr = 0;
  try {
    flowDotJsWasmModule.HEAPU8.set(request, requestPtr);
    responsePtr = flowDotJsCall(requestPtr, request.length);
    updateFlowDotJsMemoryViews();
    const response = JSON.parse(decodeHeapString(responsePtr));
    if (!response.ok) {
      throw new Error(response.error);
    }
    return response.value;

View on GitHub (pinned to f88ac94bcf)