Link Check
VMark verifies that local link and image targets in your markdown actually exist on disk. Runs alongside the markdown lint engine on Alt + Mod + V or View → Check Markdown.
What it checks
For every local link and image in the document:
[text](./other.md)— the file./other.mdresolves and exists— the image file exists[text](./other.md#section)— the file exists (anchor checking is handled by thelinkFragmentsrule)
When a target is missing, an entry appears in the lint badge and in F2 / Shift + F2 navigation. How it is drawn depends on the mode: in Source mode the link gets CodeMirror's red diagnostic underline and a gutter marker; in WYSIWYG mode the whole block containing the link is marked with a red bar along its left edge and a faint tint — WYSIWYG lint marks are block-level, never an inline underline.
What it skips
- Fragment-only links (
#anchor) — handled by thelinkFragmentsrule which checks against the current document's headings - External URLs — any URI scheme (
http:,https:,mailto:,obsidian:,vscode:, …) and protocol-relative//host/…URLs. Windows drive-letter paths (C:\…,C:/…) are still checked as file paths - Untitled documents — without a saved file path, relative URLs can't be resolved against any directory
How resolution works
Link Check resolves paths relative to the source file's directory:
Link in /repo/docs/intro.md | Resolves to |
|---|---|
[a](./other.md) | /repo/docs/other.md |
[a](../shared.md) | /repo/shared.md |
[a](images/logo.png) | /repo/docs/images/logo.png |
[a](/docs/intro.md) | /repo/docs/docs/intro.md (rooted as relative within the file's dir) |
Fragments are stripped before file lookup — [a](./other.md#section) checks ./other.md only.
Percent-encoded paths are decoded before lookup —  checks photo one.png, matching how the editor renders it.
Performance
- Async — runs in parallel with the sync rules; results merge in when ready
- Deduped — each unique resolved path is checked once per run, even if linked multiple times
- No keystroke triggering — fs.exists on every keystroke would thrash; runs only on the explicit lint trigger
- Operational error tolerance — if
fs.existsthrows (permission denied, capability scope issue), the result iserror(skipped), notmissing. Better silent than wrong.
Diagnostic codes
| Code | Severity | Trigger |
|---|---|---|
| M001 | Error | Image file not found at resolved local path |
| M002 | Error | Linked file not found at resolved local path |
See also
- Markdown Lint — full rule reference
- Settings → Markdown → Lint