{"record":{"id":"320aac87dc1935b4","repo":"Automattic/harper","slug":"workerlinter-has-been-disposed","errorCode":null,"errorMessage":"WorkerLinter has been disposed.","messagePattern":"WorkerLinter has been disposed\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/harper.js/src/WorkerLinter/index.ts","lineNumber":244,"sourceCode":"\t\treturn await this.rpc('loadWeirpackFromBytes', [arr]);\n\t}\n\n\t/**\n\t * Load a Weirpack from bytes via the worker thread.\n\t *\n\t * Returns the failure report if tests fail or `undefined` when the pack is imported.\n\t */\n\tasync loadWeirpackFromBytes(\n\t\tbytes: Uint8Array | number[],\n\t): Promise<WeirpackTestFailures | undefined> {\n\t\tconst arr = Array.from(bytes);\n\t\treturn await this.rpc('loadWeirpackFromBytes', [arr]);\n\t}\n\n\t/** Run a procedure on the remote worker. */\n\tprivate async rpc(procName: string, args: unknown[]): Promise<any> {\n\t\tif (this.disposed) {\n\t\t\tthrow new Error('WorkerLinter has been disposed.');\n\t\t}\n\n\t\tconst promise = new Promise((resolve, reject) => {\n\t\t\tthis.requestQueue.push({\n\t\t\t\tresolve,\n\t\t\t\treject,\n\t\t\t\trequest: { procName, args },\n\t\t\t});\n\n\t\t\tthis.submitRemainingRequests();\n\t\t});\n\n\t\treturn promise;\n\t}\n\n\tprivate async submitRemainingRequests() {\n\t\tif (this.working) {\n\t\t\treturn;","sourceCodeStart":226,"sourceCodeEnd":262,"githubUrl":"https://github.com/Automattic/harper/blob/5fe7d5ab76492d83f3ecdbc3f1da83c75dcb6f83/packages/harper.js/src/WorkerLinter/index.ts#L226-L262","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\nuseEffect(() => () => linter.dispose(), []); // disposed, then reused\n// after\nconst ref = useRef<WorkerLinter>();\nuseEffect(() => {\n  ref.current = new WorkerLinter({ binary: wasm });\n  return () => { ref.current?.dispose(); ref.current = undefined; };\n}, []);\n// at call site: ref.current ??= new WorkerLinter(...);","handlingStrategy":"try-catch","validationCode":"function ensureUsable(linter: WorkerLinter) {\n  if (!linter) throw new Error('linter not initialized');\n  return linter;\n}","typeGuard":null,"tryCatchPattern":"try {\n  return await linter.lint(text);\n} catch (e) {\n  if (e instanceof Error && e.message === 'WorkerLinter has been disposed.') {\n    linter = createLinter(); // recreate after dispose\n    return await linter.lint(text);\n  }\n  throw e;\n}","preventionTips":["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"],"tags":["worker","lifecycle","disposed"],"backgroundTag":"invalid-state-transition","analyzedSha":"5fe7d5ab76492d83f3ecdbc3f1da83c75dcb6f83","analyzedAt":"2026-09-06T11:34:38.610Z","contentChangedAt":"2026-09-06T11:34:38.610Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}