Automattic/harper · error · Error
WorkerLinter has been disposed.
Error message
WorkerLinter has been disposed.
What it means
WorkerLinter.rpc checks this.disposed before queueing any request to the worker. After calling linter.dispose(), every public method (lint, organizedLints, applySuggestion, isLikelyEnglish, isolateEnglish, loadWeirpackFromBytes, and setup itself) throws 'WorkerLinter has been disposed.' to prevent use of a terminated worker.
Source
Thrown at packages/harper.js/src/WorkerLinter/index.ts:244
return await this.rpc('loadWeirpackFromBytes', [arr]);
}
/**
* Load a Weirpack from bytes via the worker thread.
*
* Returns the failure report if tests fail or `undefined` when the pack is imported.
*/
async loadWeirpackFromBytes(
bytes: Uint8Array | number[],
): Promise<WeirpackTestFailures | undefined> {
const arr = Array.from(bytes);
return await this.rpc('loadWeirpackFromBytes', [arr]);
}
/** Run a procedure on the remote worker. */
private async rpc(procName: string, args: unknown[]): Promise<any> {
if (this.disposed) {
throw new Error('WorkerLinter has been disposed.');
}
const promise = new Promise((resolve, reject) => {
this.requestQueue.push({
resolve,
reject,
request: { procName, args },
});
this.submitRemainingRequests();
});
return promise;
}
private async submitRemainingRequests() {
if (this.working) {
return;View on GitHub (pinned to 5fe7d5ab76)
Solutions
- Don't call methods after dispose(); recreate a new WorkerLinter instance if you need to lint again
- Guard calls with a disposed flag or store the instance in a ref that is nulled on cleanup
- Await in-flight lint promises before disposing, or catch this error in cleanup racing
- Reset the singleton on HMR/StrictMode remount instead of reusing the disposed one
Example fix
// before
useEffect(() => () => linter.dispose(), []); // disposed, then reused
// after
const ref = useRef<WorkerLinter>();
useEffect(() => {
ref.current = new WorkerLinter({ binary: wasm });
return () => { ref.current?.dispose(); ref.current = undefined; };
}, []);
// at call site: ref.current ??= new WorkerLinter(...); Defensive patterns
Strategy: try-catch
Validate before calling
function ensureUsable(linter: WorkerLinter) {
if (!linter) throw new Error('linter not initialized');
return linter;
} Try / catch
try {
return await linter.lint(text);
} catch (e) {
if (e instanceof Error && e.message === 'WorkerLinter has been disposed.') {
linter = createLinter(); // recreate after dispose
return await linter.lint(text);
}
throw e;
} Prevention
- Null out the linter reference when you dispose it and lazily recreate on next use
- In React, keep the linter in a ref keyed to the effect and handle StrictMode double-mount
- Avoid disposing while lint promises are still pending
When it happens
Trigger: Calling any WorkerLinter method after linter.dispose() — commonly in React effects whose cleanup disposes the linter while async lint calls are still in flight, or reusing a cached linter after page/navigation teardown.
Common situations: React 18 StrictMode double mount/unmount disposing the linter; disposing on unmount while a debounced lint is pending; framework hot reloads recreating components but reusing a disposed singleton.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- Unhandled case: type undefined
- Unhandled case: ${arg}
- Unhandled case: ${requestArg.type}
- Expected binary to be a string of url but got ${typeof binar
- Expected glue flavor to be "full" or "slim" but got ${glueFl
AI-assisted analysis of Automattic/harper@5fe7d5ab76 (2026-09-06).
Data as JSON: /api/errors/320aac87dc1935b4.
Report an issue: GitHub.