Skip to content

[BUG] refreshIndex() crashes on deleted files - notesToReindex not cleared by vault delete handler #536

@BluFaze

Description

@BluFaze

Problem description:

Omnisearch throws unhandled errors when it tries to reindex files that were deleted between being queued for reindex and refreshIndex() actually running. This is a race condition in the reindex queue lifecycle.

How this happens in practice:

This is triggered by any plugin that modifies a file and then deletes it shortly after — which is a very common pattern:

  • Sync plugins: update a file's frontmatter (e.g., marking it as synced), then discover the remote item was deleted, so they delete the local file/folder via vault.trash()
  • File organizer plugins: read/modify a file, then move or clean it up
  • Batch processing plugins: process content, then remove temporary files

In my case, a sync plugin updates MOC files in a project folder (triggering vault.modify()), then deletes the entire project folder because the corresponding remote project was removed. Omnisearch's modify handler queues the file for reindex, but the delete handler doesn't dequeue it — so refreshIndex() later tries to reindex a file that no longer exists.

Event sequence (step by step):

1. Plugin modifies "02_PROJECTS/MyProject/🚀 MyProject.md"
→ Obsidian fires vault 'modify' event
→ Omnisearch's modify handler runs:
notesIndexer.flagNoteForReindex(tFileRef)
The TFile OBJECT is stored in a Set (notesToReindex)2. Plugin deletes the project folder (including that file)
→ Obsidian fires vault 'delete' event
→ Omnisearch's delete handler runs:
documentsRepository.removeDocument(path)    ✅ cleaned
searchEngine.removeFromPaths([path])         ✅ cleaned       embedsRepository.removeFile(path)            ✅ cleaned       notesIndexer.notesToReindex                  ❌ NOT cleaned
The stale TFile reference remains in the Set.3. User switches to another window (or back)
→ Browser 'blur' event fires
→ Omnisearch calls refreshIndex()
→ Iterates notesToReindex, finds stale TFile with path still set
→ Calls addDocument(stalePath)     → getAndMapIndexedDocument(stalePath)       → vault.getAbstractFileByPath(stalePath) returns null
→ throws Error("Invalid file path: ...")   💥

Note: refreshIndex() is bound to the window blur event — it fires every time the user switches between Obsidian and any other window (browser, terminal, etc.). This makes the race condition very easy to hit, since users typically switch windows frequently.

Console errors:

Omnisearch: Error while adding "02_PROJECTS/ProjectName/🚀 ProjectName.md" to live cacheError: Invalid file path: "02_PROJECTS/ProjectName/🚀 ProjectName.md"    at xh.getAndMapIndexedDocument (plugin:omnisearch:133:186006)
at xh.addDocument (plugin:omnisearch:133:186727)
at Th.addFromPaths (plugin:omnisearch:133:179966)
at Ah.refreshIndex (plugin:omnisearch:135:1838)
Omnisearch 02_PROJECTS/ProjectName/🚀 ProjectName.md cannot be read

If multiple files are deleted in a batch (e.g., an entire folder), the internal error counter accumulates and triggers the misleading Notice: "There might be an issue with your cache. You should clean it in Omnisearch settings and restart Obsidian." — even though the cache is fine.

Root cause (source-level):

The delete vault handler cleans up 3 out of 4 internal stores, missing the reindex queue:

// Current delete handler:this.app.vault.on("delete", (file) => {
if (file instanceof TFile) {
this.documentsRepository.removeDocument(file.path);    // ✅
searchEngine.removeFromPaths([file.path]);              // ✅        this.embedsRepository.removeFile(file.path);            // ✅        // ❌ Missing: notesIndexer.notesToReindex cleanup    }});

And getAndMapIndexedDocument() throws instead of returning null when the file is gone:

async getAndMapIndexedDocument(path) {
let file = app.vault.getAbstractFileByPath(path);
if (!file) throw new Error(`Invalid file path: "${path}"`);
// Should gracefully return null here}

Suggested fix (2 layers):

Layer 1 — Queue cleanup on delete (fixes the root cause):

// In vault 'delete' handler, add:this.notesIndexer.notesToReindex = new Set(
[...this.notesIndexer.notesToReindex].filter(f => f.path ! file.path));// Using path comparison instead of reference equality for robustness

Layer 2 — Defensive null handling (safety net for any other edge case):

// getAndMapIndexedDocument:if (!file) return null;  // instead of throw

// addDocument:let doc = await this.getAndMapIndexedDocument(path);if (!doc) return;  // null guard before accessing .path

Both layers are needed: Layer 1 prevents unnecessary work, Layer 2 ensures graceful degradation if a file disappears through any other code path.

Reproduction steps:

  1. Install a plugin that programmatically modifies then deletes files (e.g., a sync plugin, or test with a simple script that calls vault.modify() then vault.trash())
  2. Trigger a file deletion that includes recently-modified files
  3. Switch to another application window (this fires the blur event that triggers refreshIndex())
  4. Switch back to Obsidian → check the developer console (Ctrl/Cmd+Shift+I)
  5. You'll see Error: Invalid file path for each deleted file that was in the reindex queue
  6. If 5+ files were affected: the "cache issue" Notice appears

Your environment:

  • Omnisearch version: 1.28.2
  • Obsidian version: 1.8.9
  • Operating system: macOS 26.4 (Apple Silicon)
  • Number of indexed documents: ~1000

Things to try:

  • Does the problem occur when Omnisearch is the only active community plugin:No — requires another plugin to trigger vault.modify() followed by vault.trash() programmatically
  • Does the problem occur when you don't index PDFs, images, or other non-notes files: Yes — affects .md files
  • Does the problem occur after a cache reset: Yes — the issue is in the live reindex queue (notesToReindex Set), not in the persisted cache

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions