perspective-dev/perspective · error · Error

Missing perspective-client.wasm

Error message

Missing perspective-client.wasm

What it means

The DuckDBServer virtual server requires the perspective WASM client module (perspective-client.wasm) to execute DuckDB in WebAssembly. The constructor retrieves the module from the registered `<perspective-viewer>` custom element's `__wasm_module__` property. If the element is registered but the module was never attached, the constructor throws this error.

Solutions

  1. Import and fully initialize @finos/perspective-viewer (await its init/load of perspective-client.wasm) before constructing DuckDBServer.
  2. Align versions of @finos/perspective-viewer and perspective-js (same release) and reinstall node_modules.
  3. Manually fetch perspective-client.wasm and set it on the viewer class: `customElements.get("perspective-viewer").__wasm_module__ = mod;`.
  4. Confirm your bundler (webpack/vite) is configured to emit and load the .wasm asset so viewer init actually completes.

Example fix

// before
const server = new DuckDBServer(db); // viewer registered but wasm never attached

// after
await import("@finos/perspective-viewer");
await customElements.whenDefined("perspective-viewer");
if (!customElements.get("perspective-viewer").__wasm_module__) {
  const mod = await WebAssembly.compileStreaming(fetch("perspective-client.wasm"));
  customElements.get("perspective-viewer").__wasm_module__ = mod;
}
const server = new DuckDBServer(db);
Defensive patterns

Strategy: validation

Validate before calling

async function ensureDuckDBServerReady() {
  await customElements.whenDefined("perspective-viewer");
  const viewer = customElements.get("perspective-viewer");
  if (!viewer || !viewer.__wasm_module__) {
    throw new Error("perspective WASM not attached; await viewer init first");
  }
}

Type guard

function viewerHasWasm(el: unknown): el is (new () => HTMLElement) & { __wasm_module__: WebAssembly.Module } {
  return typeof el === 'function' && (el as any).__wasm_module__ != null;
}

Try / catch

try {
  const server = new DuckDBServer(db);
} catch (e) {
  if (e.message === "Missing perspective-client.wasm") {
    await loadPerspectiveViewerWasm();
    const server = new DuckDBServer(db); // retry once after init
  } else throw e;
}

Prevention

When it happens

Trigger: Constructing a DuckDBServer while `customElements` is defined, `customElements.get("perspective-viewer")` resolves to a registered class lacking `__wasm_module__` — the viewer web component was registered without its WASM payload being fetched/initialized.

Common situations: Registering the viewer component without awaiting its WASM init; version mismatch between @finos/perspective-viewer and perspective-js so `__wasm_module__` is never set; running under a test harness or polyfill that registers a placeholder `perspective-viewer` element; bundler config stripping the WASM asset so initialization silently no-ops.

Related errors


AI-assisted analysis of perspective-dev/perspective@11c8238c0c (2026-09-09). Data as JSON: /api/errors/81eac7eb7956c7d7. Report an issue: GitHub.

Appendix: source

Thrown at rust/perspective-js/src/ts/virtual_servers/duckdb.ts:278

        throw error;
    }
}

/**
 * An implementation of Perspective's Virtual Server for `@duckdb/duckdb-wasm`.
 */
export class DuckDBHandler implements perspective.VirtualServerHandler {
    private db: duckdb.AsyncDuckDBConnection;
    private sqlBuilder: perspective.GenericSQLVirtualServerModel;
    constructor(db: duckdb.AsyncDuckDBConnection, mod?: typeof perspective) {
        if (!mod) {
            if (customElements) {
                const viewer_class: any =
                    customElements.get("perspective-viewer");
                if (viewer_class) {
                    mod = viewer_class.__wasm_module__;
                } else {
                    throw new Error("Missing perspective-client.wasm");
                }
            } else {
            }
        }

        this.db = db;
        this.sqlBuilder = new mod!.GenericSQLVirtualServerModel({
            column_separator: "|",
            like_escape_clause: "\\",
            regex_fn: "regexp_matches",
        });
    }

    getFeatures(): perspective.Features {
        return {
            group_by: true,
            split_by: true,
            sort: true,

View on GitHub (pinned to 11c8238c0c)