Automattic/harper · error · TypeError
Expected binary to be a string of url but got ${typeof binar
Error message
Expected binary to be a string of url but got ${typeof binaryUrl}. What it means
The WorkerLinter worker script expects the first message's data array to start with a string URL pointing at the WASM binary. If e.data[0] is not a string, it throws this TypeError instead of initializing SuperBinaryModule. This validates the bootstrap handshake between WorkerLinter (main thread) and worker.ts.
Source
Thrown at packages/harper.js/src/WorkerLinter/worker.ts:13
/// <reference lib="webworker" />
import './shims';
import { SuperBinaryModule, type WasmGlueFlavor } from '../BinaryModule';
import LocalLinter from '../LocalLinter';
import Serializer, { isSerializedRequest, type SerializedRequest } from '../Serializer';
// Notify the main thread that we are ready
self.postMessage('ready');
self.onmessage = (e) => {
const [binaryUrl, dialect, glueFlavor] = e.data;
if (typeof binaryUrl !== 'string') {
throw new TypeError(`Expected binary to be a string of url but got ${typeof binaryUrl}.`);
}
if (glueFlavor !== undefined && glueFlavor !== 'full' && glueFlavor !== 'slim') {
throw new TypeError(`Expected glue flavor to be "full" or "slim" but got ${glueFlavor}.`);
}
const binary = SuperBinaryModule.create(binaryUrl, glueFlavor as WasmGlueFlavor | undefined);
const serializer = new Serializer(binary);
const linter = new LocalLinter({ binary, dialect });
async function processRequest(v: SerializedRequest) {
const { procName, args } = await serializer.deserialize(v);
if (procName in linter) {
// @ts-expect-error
const res = await linter[procName](...args);
postMessage(await serializer.serializeArg(res));
}
}
View on GitHub (pinned to 5fe7d5ab76)
Solutions
- Pass a string URL to the WASM binary, e.g. new WorkerLinter({ binary: wasmUrl }) or new WorkerLinter({ binary: new URL('./wasm.wasm', import.meta.url).toString() })
- If you have bytes rather than a URL, convert/upload to a URL the worker can fetch (URL.createObjectURL(new Blob([bytes])))
- Check WorkerLinter constructor docs for the expected binary option shape
- Verify the value isn't undefined due to a failed dynamic import or env variable
Example fix
// before
const linter = new WorkerLinter({ binary: await import('./harper_wasm_bg.wasm') });
// after
const linter = new WorkerLinter({ binary: new URL('./harper_wasm_bg.wasm', import.meta.url).toString() }); Defensive patterns
Strategy: type-guard
Validate before calling
const binary = new URL('./harper_wasm_bg.wasm', import.meta.url).toString();
if (typeof binary !== 'string') throw new Error('binary must be a string URL'); Type guard
function isBinaryUrl(v: unknown): v is string {
return typeof v === 'string' && (v.startsWith('/') || v.startsWith('http') || v.startsWith('blob:') || v.startsWith('data:'));
} Try / catch
try {
const linter = new WorkerLinter({ binary });
await linter.setup();
} catch (e) {
if (e instanceof TypeError && e.message.includes('Expected binary to be a string of url')) {
console.error('WorkerLinter binary option must be a URL string, got:', typeof binary);
}
throw e;
} Prevention
- Always pass a string URL (new URL(...).toString()) for the binary option
- Don't pass imported WASM modules or ArrayBuffers directly to WorkerLinter
- Check for undefined caused by failed dynamic imports or missing env vars
When it happens
Trigger: Constructing WorkerLinter with a binary option that is not a string URL (e.g. passing an ArrayBuffer, a URL object, undefined, or a WebAssembly.Module); calling worker.postMessage manually with a malformed [binaryUrl, dialect, glueFlavor] tuple.
Common situations: Config mistakes like new WorkerLinter({ binary: wasmBytes }) instead of a URL to the .wasm asset; bundler import returning a module default rather than a URL string; forgetting to pass binary so it is undefined in dev.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- Expected glue flavor to be "full" or "slim" but got ${glueFl
- Unhandled case: type undefined
- Unhandled case: ${arg}
- Unhandled case: ${requestArg.type}
- WorkerLinter has been disposed.
AI-assisted analysis of Automattic/harper@5fe7d5ab76 (2026-09-06).
Data as JSON: /api/errors/88647da8af08385d.
Report an issue: GitHub.