Installing a Package From GitHub, With a Keyword That Never Took Effect

Seven packages in my Emacs config are fetched with package-vc-install instead of the ordinary archive-based use-package foo :ensure t: auto-package-update, powerline, centaur-tabs, lsp-mode, php-mode, dap-mode, and smartparens-mode, each cloned straight from its GitHub repository and each guarded by the same unless (file-directory-p ...) check so the clone only happens once. All seven of those live together in core/packages.el, the one file whose whole job is bootstrapping how packages get installed. An eighth package installed the same way, auto-complete, lives somewhere else entirely — core/ui.el, mixed in with theme settings and completion configuration, nowhere near the other seven.

The call itself, and a keyword that doesn't match its siblings

core/ui.el has this ahead of the package's own use-package block:

  • (unless (file-directory-p "~/.emacs.d/elpa/auto-complete/")
  • (package-vc-install
  • '(auto-complete :url "https://github.com/auto-complete/auto-complete.git"
  • :listp-dir "lisp"))
  • )

I checked all seven to be sure before writing this: every single one follows the identical two-line shape, (unless (file-directory-p ...) (package-vc-install '(name :url "..."))), nothing more. The other seven calls in packages.el pass only a package name and :url — no extra keys at all. This one adds a third key, :listp-dir, apparently trying to tell package-vc-install that the package's Lisp source lives in a lisp subdirectory of the cloned repository rather than at its root. The actual keyword package-vc-install documents for that purpose is :lisp-dir, not :listp-dir. Plist-based configuration in Lisp doesn't generally raise an error for an unrecognized key — it just gets ignored, silently, which is exactly the kind of typo that can survive indefinitely: the install still succeeds using package-vc-install's own default directory-detection logic, so nothing about running this line ever signals that one of its three arguments was never actually read.

The workaround one hook away

A few lines later, still in ui.el:

  • ;; https://github.com/auto-complete/auto-complete/issues/533
  • (add-hook 'auto-complete-mode-hook
  • (lambda ()
  • (setq ac-sources (remove 'ac-source-abbrev ac-sources))))

That comment is a citation, not decoration — a specific, real upstream issue number on the exact package this config fetches from source, sitting right next to the code that works around it. The hook strips one particular completion source, ac-source-abbrev, out of the active list every time auto-complete-mode turns on. Whatever issue #533 actually describes, someone decided the fix belonged here, applied automatically on every activation, with the issue number left behind as the reason rather than a bare unexplained remove call.

What the misplaced keyword actually costs

None of this breaks anything today, which is exactly why it's worth writing about the pattern rather than the specific bug. A stray, unrecognized plist key is the Lisp equivalent of a comment nobody reads: syntactically present, semantically inert, and completely invisible unless someone happens to already know what the correct key is supposed to be and goes looking for a mismatch. If auto-complete's actual Lisp files genuinely sit outside a lisp/ subdirectory — which package-vc-install's own fallback detection would need to figure out on its own, since the key meant to tell it explicitly never took effect — the install still works, just not for the reason the config appears to claim it works.

The more useful habit than "check for typos" is checking whether a configuration language will actually complain when you get a key wrong. Emacs Lisp's plists won't; several popular configuration formats — Kubernetes manifests under strict schema validation, for instance — will refuse to apply at all with an unrecognized field. Knowing which category a given tool falls into changes how much you can trust "it installed without error" as proof that every argument you wrote actually did something. Here, it wasn't proof of anything except that the clone succeeded — the one keyword meant to fine-tune where the Lisp source lives has been silently ignored for as long as this line has existed, with the eventual correct behavior arrived at by accident rather than by that argument doing its job.

Add new comment

Restricted HTML

  • Allowed HTML tags: <a href hreflang> <em> <strong> <cite> <blockquote cite> <code> <ul type> <ol start type> <li> <dl> <dt> <dd> <h2 id> <h3 id> <h4 id> <h5 id> <h6 id>
  • Lines and paragraphs break automatically.
  • Web page addresses and email addresses turn into links automatically.
Please share this article on your favorite website or platform.