Why
try_lint() fires on every BufEnter and BufWritePost; without a timeout, a single hung linter process (e.g. mypy waiting on a slow type stub download, or tflint waiting on its registry) pins Neovim's job infrastructure indefinitely and silently.
Current state
The autocmd in lua/config/plugins/specs/nvim-lint.lua calls lint.try_lint() with no arguments on BufEnter and BufWritePost. The nvim-lint plugin accepts an optional { timeout = <ms> } table as the first argument to bound the linter subprocess, but this config omits it entirely, leaving the runtime default — which is no timeout. External linters like mypy, clippy, and tflint all make network calls or scan large dependency trees and can stall.
Ideal state
try_lint() is called with an explicit timeout value (e.g. lint.try_lint(nil, { timeout = 5000 })).
- A linter that does not complete within the timeout is killed rather than left running.
- Neovim continues to function normally after the timeout — no accumulated zombie linter processes.
Out of scope
- Per-linter timeout tuning — a single shared timeout covers all current linters.
- Adding retry logic for timed-out lint runs.
Starting points
lua/config/plugins/specs/nvim-lint.lua — the autocmd callback that calls try_lint() (the only change needed)
- nvim-lint README — documents the
timeout option accepted by try_lint
QA plan
- Set
timeout = 1 (1 ms) and open any file that triggers a linter (e.g. a .py file with mypy configured) — expect the linter to report a timeout or silently produce no diagnostics within 1 ms rather than hanging.
- Restore
timeout = 5000 and open the same file — expect lint diagnostics to appear normally within 5 seconds.
- Open a file while offline (to force
tflint registry lookup to stall) — expect no Neovim hang; the linter should time out within the configured window.
Done when
try_lint() is called with an explicit timeout value and a stalled linter process is killed within that window rather than blocking indefinitely.
Why
try_lint()fires on everyBufEnterandBufWritePost; without a timeout, a single hung linter process (e.g.mypywaiting on a slow type stub download, ortflintwaiting on its registry) pins Neovim's job infrastructure indefinitely and silently.Current state
The autocmd in
lua/config/plugins/specs/nvim-lint.luacallslint.try_lint()with no arguments onBufEnterandBufWritePost. The nvim-lint plugin accepts an optional{ timeout = <ms> }table as the first argument to bound the linter subprocess, but this config omits it entirely, leaving the runtime default — which is no timeout. External linters likemypy,clippy, andtflintall make network calls or scan large dependency trees and can stall.Ideal state
try_lint()is called with an explicittimeoutvalue (e.g.lint.try_lint(nil, { timeout = 5000 })).Out of scope
Starting points
lua/config/plugins/specs/nvim-lint.lua— the autocmd callback that callstry_lint()(the only change needed)timeoutoption accepted bytry_lintQA plan
timeout = 1(1 ms) and open any file that triggers a linter (e.g. a.pyfile withmypyconfigured) — expect the linter to report a timeout or silently produce no diagnostics within 1 ms rather than hanging.timeout = 5000and open the same file — expect lint diagnostics to appear normally within 5 seconds.tflintregistry lookup to stall) — expect no Neovim hang; the linter should time out within the configured window.Done when
try_lint()is called with an explicittimeoutvalue and a stalled linter process is killed within that window rather than blocking indefinitely.