Critique your plugin #3 — Obsidian October 2024
Based on Obsidian's video on YouTube. If you like this content, support the original creators by watching, liking and subscribing to their content.
Avoid editing vault files with separate `vault.read` and `vault.modify` calls when other plug-ins might touch the same file; it can cause last-writer-wins data loss.
Briefing
Obsidian’s plug-in API has two recurring failure points—file edits that aren’t atomic and lifecycle code that runs at the wrong time. The biggest practical risk is data loss when multiple plug-ins modify the same vault file concurrently. A common pattern is calling `vault.read` to load a file, editing the string in memory, then calling `vault.modify` to write it back. That approach works until another plug-in performs the same read/modify sequence on the same file: both can read the original contents, but whichever plug-in writes last overwrites the other’s changes, effectively racing to “win” the disk write.
The fix is to use the vault “process” API, which pairs the read and write inside a single atomic transaction queued through Obsidian’s file system adapter. Because the file system adapter serializes disk operations, the process API ensures that the callback receives the latest on-disk contents and that the returned modified string is saved as one indivisible operation. Instead of manually orchestrating read-then-write, the callback function performs transformations on the provided contents and returns the updated text; Obsidian then commits it safely in the operations queue. This prevents other plug-ins from interleaving their own edits between the read and the write.
The second major source of confusion is the `onunload` lifecycle hook. Many developers mistakenly treat it like a “close Obsidian” event, but `onunload` fires only in two cases: when a plug-in is disabled (manually toggled off or uninstalled) and when the plug-in is updated. Updates are especially easy to misunderstand: the old version must be unloaded so the new version can be loaded cleanly. That means `onunload` is primarily for cleanup—removing global event listeners, releasing resources, and avoiding memory leaks.
Where things go wrong is putting “action” logic in `onunload`. The hook should not be used to save data to disk, and it’s also the wrong place to detach custom UI elements in a way that assumes a disable-only scenario. Developers often call something like `detachLeavesOfType` for their custom view types inside `onunload`, expecting it to remove UI only when the plug-in is turned off. But because `onunload` also runs during updates, detaching leaves during an update can wipe out user layout state. After the update, `onload` may recreate the view in a default sidebar position, discarding any custom placement.
As of Obsidian 1.7, plug-in custom views are automatically unloaded when a plug-in is disabled or uninstalled, removing the need for developers to manually disambiguate disable vs update. With that change, `onunload` can focus on cleanup, while Obsidian handles unloading custom views, preserving user layout state across updates. The result is a plug-in that edits files safely and manages lifecycle events without erasing the workspace the user built.
Cornell Notes
The core guidance is to avoid two common plug-in mistakes in Obsidian: non-atomic file edits and misuse of the `onunload` hook. Editing a file by doing `vault.read`, modifying the string, then `vault.modify` can lose changes when multiple plug-ins touch the same file concurrently. The vault “process” API fixes this by running the read-and-write as a single atomic transaction through the file system adapter’s queued operations. Separately, `onunload` does not run when closing Obsidian; it runs only on disable/uninstall and on plug-in updates. As of Obsidian 1.7, custom views are automatically unloaded on disable/uninstall, so manual detaching for layout cleanup is often unnecessary and can otherwise wipe user placement during updates.
Why does the simple `vault.read` → modify string → `vault.modify` pattern risk losing data?
How does the vault process API prevent concurrent-edit races?
When does `onunload` actually run in Obsidian plug-ins?
What belongs in `onunload`, and what should be avoided?
Why can detaching custom leaves in `onunload` break user layout after updates?
What changed in Obsidian 1.7 that affects custom view unloading?
Review Questions
- What specific concurrency problem occurs when two plug-ins both use `vault.read` followed by `vault.modify` on the same file, and how does the process API change the execution model?
- List the two situations that trigger `onunload`, and describe why updates make it easy to misuse this hook.
- How does Obsidian 1.7 change the recommended approach to detaching custom leaves or preserving custom view placement across plug-in updates?
Key Points
- 1
Avoid editing vault files with separate `vault.read` and `vault.modify` calls when other plug-ins might touch the same file; it can cause last-writer-wins data loss.
- 2
Use the vault process API so the read-and-write happen as a single atomic transaction in the file system adapter’s queued operations.
- 3
Treat `onunload` as a cleanup hook for disable/uninstall and updates—not as a “close Obsidian” event.
- 4
Do not perform disk-saving actions inside `onunload`; focus on releasing resources and removing global event listeners to prevent memory leaks.
- 5
Be careful detaching custom views in `onunload`, because the same hook runs during updates and can reset user layout state.
- 6
With Obsidian 1.7, custom views are automatically unloaded on disable/uninstall, reducing the need for manual detaching logic tied to lifecycle differences.