{"record":{"id":"4db2ce095ba8b17a","repo":"dmtrKovalenko/fff","slug":"filefinder-instance-has-been-destroyed","errorCode":null,"errorMessage":"FileFinder instance has been destroyed.","messagePattern":"FileFinder instance has been destroyed\\.","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/fff-bun/src/finder.ts","lineNumber":186,"sourceCode":"      // benign id-map miss before the trampoline is closed.\n      this.watchJsCallback?.close();\n      this.watchJsCallback = null;\n    }\n  }\n\n  /**\n   * Check if this instance has been destroyed.\n   */\n  get isDestroyed(): boolean {\n    return this.handle === null;\n  }\n\n  /**\n   * Guard that returns an error if the instance has been destroyed.\n   */\n  private ensureAlive(): Result<NativeHandle> {\n    if (this.handle === null) {\n      return err(\"FileFinder instance has been destroyed.\");\n    }\n    return { ok: true, value: this.handle };\n  }\n\n  /**\n   * Search for files matching the query.\n   *\n   * The query supports fuzzy matching and special syntax:\n   * - `foo bar` - Match files containing \"foo\" and \"bar\"\n   * - `src/` - Match files in src directory\n   * - `file.ts:42` - Match file.ts with line 42\n   * - `file.ts:42:10` - Match file.ts with line 42, column 10\n   *\n   * @param query - Search query string\n   * @param options - Search options\n   * @returns Search results with matched files and scores\n   *\n   * @example","sourceCodeStart":168,"sourceCodeEnd":204,"githubUrl":"https://github.com/dmtrKovalenko/fff/blob/7f8537e70f0ea1210f9acbbfc4640141105cdc78/packages/fff-bun/src/finder.ts#L168-L204","documentation":"FileFinder wraps a native handle that becomes null after destroy(). ensureAlive is the guard every method runs through; if the instance was already destroyed (or never successfully created), any further operation returns this error instead of dereferencing a null native handle.","triggerScenarios":"Calling any FileFinder method (search, grep, getScanProgress, watch, destroy, etc.) after destroy() was called, or after a failed async init that never assigned this.handle.","commonSituations":"Event handlers or timers firing after teardown; calling search from a stale reference cached elsewhere; awaiting an operation while another code path destroyed the finder; double-destroy in cleanup logic.","solutions":["Check the returned Result and stop using the instance after this error; create a new FileFinder if needed.","Set this.handle = null-sentinel ownership in one place: after destroy(), remove all references other components hold.","Cancel pending async work (intervals, queued searches) in your destroy/teardown path before or right after calling destroy()."],"exampleFix":"// before\nasync function refresh(finder) { return finder.search(\"foo\"); } // finder may be destroyed\n// after\nasync function refresh(finder) {\n  const alive = finder.ensureAlive();\n  if (!alive.ok) return; // instance destroyed, skip\n  return finder.search(\"foo\");\n}","handlingStrategy":"try-catch","validationCode":"if (finder === null) throw new Error('FileFinder instance has been destroyed.');","typeGuard":"function isUsableFinder(f) { return f instanceof FileFinder && f.handle !== null; }","tryCatchPattern":"try { const r = finder.search(q); } catch (e) { if (e.message.includes('has been destroyed')) { finder = await FileFinder.create(opts); return finder.search(q); } }","preventionTips":["Null out every reference to the finder after destroy().","Cancel async work (intervals, queued requests) during teardown.","Wrap native calls in a helper that checks aliveness before delegating."],"tags":["lifecycle","use-after-destroy","typescript","null-pointer"],"backgroundTag":"invalid-state-transition","analyzedSha":"7f8537e70f0ea1210f9acbbfc4640141105cdc78","analyzedAt":"2026-09-10T07:26:53.407Z","contentChangedAt":"2026-09-10T07:26:53.407Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}