Skip to content

nvim-lint: try_lint() has no timeout — stalled linter process blocks indefinitely on every buffer event #92

@ooloth

Description

@ooloth

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

  1. 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.
  2. Restore timeout = 5000 and open the same file — expect lint diagnostics to appear normally within 5 seconds.
  3. 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.

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions