Skip to content

[audit] PackChanged autocmd in fff.nvim setup is a top-level event — should be User PackChanged — binary never downloaded on install #117

Description

@stanfish06

What

lua/config/plugin_config.lua lines 149–161 register an autocmd to download/build the fff.nvim binary after install or update:

local fff_ok, _ = pcall(function()
    vim.api.nvim_create_autocmd("PackChanged", {
        callback = function(ev)
            local name, kind = ev.data.spec.name, ev.data.kind
            if name == "fff.nvim" and (kind == "install" or kind == "update") then
                if not ev.data.active then
                    vim.cmd.packadd("fff.nvim")
                end
                require("fff.download").download_or_build_binary()
            end
        end,
    })
end)

Where

lua/config/plugin_config.lua, lines 149–161.

Why it matters

vim.pack fires plugin lifecycle notifications as User events, not top-level Neovim events. The correct registration pattern is:

vim.api.nvim_create_autocmd("User", {
    pattern = "PackChanged",
    callback = ...,
})

Because PackChanged is not a built-in top-level Neovim event, nvim_create_autocmd("PackChanged", ...) creates an autocmd that is never fired. The pcall wrapper silently swallows any creation error, so startup succeeds — but the callback never runs. As a result, after :SyncPkgs installs or updates fff.nvim, require("fff.download").download_or_build_binary() is never called and the native binary is never built. fff will appear to load (the Lua side works) but find_files() / live_grep() will fail at runtime because the binary is missing.

Recommended action

Change the event registration to a User autocmd with a pattern:

local fff_ok, _ = pcall(function()
    vim.api.nvim_create_autocmd("User", {
        pattern = "PackChanged",
        callback = function(ev)
            local name, kind = ev.data.spec.name, ev.data.kind
            if name == "fff.nvim" and (kind == "install" or kind == "update") then
                if not ev.data.active then
                    vim.cmd.packadd("fff.nvim")
                end
                require("fff.download").download_or_build_binary()
            end
        end,
    })
end)

Also worth verifying the exact field names (ev.data.spec.name, ev.data.kind, ev.data.active) against the current vim.pack event payload, as the API is experimental and the payload shape may have changed.

As an immediate workaround, run :lua require("fff.download").download_or_build_binary() manually after each :SyncPkgs.

Activity

  1. stanfish06 commented on Jun 25, 2026

    @stanfish06
    OwnerAuthor

    Correction: the current PackChanged registration is correct

    I went back to verify this against upstream Neovim source before anyone acts on the proposed fix, and the premise of this issue doesn't hold up.

    Claim in this issue: vim.api.nvim_create_autocmd("PackChanged", {...}) in lua/config/plugin_config.lua never fires, and should instead be registered as vim.api.nvim_create_autocmd("User", { pattern = "PackChanged", ... }).

    Evidence against this:

    1. runtime/doc/autocmd.txt (checked against the v0.12.3 tag) lists PackChanged and PackChangedPre in the autocmd-events registry table alongside native events like BufEnter — i.e. they are documented as native autocmd events, not User-pattern events.

    2. runtime/lua/vim/pack.lua on neovim/neovim master shows how they're actually fired:

      local function trigger_event(p, ...)
        ...
        api.nvim_exec_autocmds(event_name, { pattern = p.path, data = data })
      end

      This calls nvim_exec_autocmds("PackChanged", ...) directly — not nvim_exec_autocmds("User", { pattern = "PackChanged", ... }). A plain User autocmd would never match this.

    So the existing code:

    vim.api.nvim_create_autocmd("PackChanged", {
        callback = function(ev)
            local name, kind = ev.data.spec.name, ev.data.kind
            if name == "fff.nvim" and (kind == "install" or kind == "update") then
                ...
            end
        end,
    })

    is correct as written. Switching it to "User" with pattern = "PackChanged" would actually break the fff.nvim binary rebuild hook, not fix it.

    Recommend closing this as invalid — happy to be corrected if there's a repro showing the hook not firing, but the source doesn't support the diagnosis.


    Generated by Claude Code

  2. stanfish06 commented on Jun 29, 2026

    @stanfish06
    OwnerAuthor

    Closing as not-planned/invalid. Re-verified independently against runtime/doc/pack.txt on neovim/neovim master: it documents PackChanged/PackChangedPre as plain native autocmd events fired via nvim_exec_autocmds("PackChanged", ...) (not User-pattern events), and gives vim.api.nvim_create_autocmd('PackChanged', { callback = hooks }) as the canonical example — matching this repo's existing registration exactly. This confirms the prior correction comment above; the code as written is correct.


    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions