Emacs fails to load my init.el file after it fails to load .emacs.d/history file from (savehist-mode 1)

Here's the minimum history file

(setq minibuffer-history '("/home/eugenekim/.emacs.d/init.el" "Enabled" #("stash" 0 5 (face magit-hash magit-section [cl-struct-magit-section stash "stash@{9}" #<marker (moves after insertion) at 918 in *magit: zibann> nil #<marker at 962 in *magit: zibann> nil nil nil n\
il nil nil [cl-struct-magit-section stashes "refs/stash" #<marker (moves after insertion) at 602 in *magit: zibann> #<marker at 615 in *magit: zibann> #<marker at 1129 in *magit: zibann> nil nil nil nil nil nil [cl-struct-magit-section status nil #<marker (moves after in\
sertion) at 1 in *magit: zibann> nil #<marker at 1129 in *magit: zibann> nil nil nil nil nil nil nil ([cl-struct-magit-section branch "feature/review-tag" #<marker (moves after insertion) at 1 in *magit: zibann> #<marker at 47 in *magit: zibann> #<marker at 125 in *magit\
: zibann> nil nil nil nil nil nil #7 ([cl-struct-magit-section branch #("origin/feature/review-tag" 0 25 (face magit-branch-remote)) #<marker (moves after insertion) at 47 in *magit: zibann> nil #<marker at 100 in *magit: zibann> nil nil nil nil nil nil #9 nil] [cl-struc\
t-magit-section tag "saleor" #<marker (moves after insertion) at 100 in *magit: zibann> nil #<marker at 124 in *magit: zibann> nil nil nil nil nil nil #9 nil])] [cl-struct-magit-section unstaged nil #<marker (moves after insertion) at 125 in *magit: zibann> #<marker at 1\
46 in *magit: zibann> #<marker at 602 in *magit: zibann> nil nil nil nil nil nil #7 ([cl-struct-magit-section file "ios/littlehome/Externals/CTAssetsPickerController" #<marker (moves after insertion) at 146 in *magit: zibann> #<marker at 207 in *magit: zibann> #<marker a\
t 339 in *magit: zibann> t nil nil nil "diff --git ios/littlehome/Externals/CTAssetsPickerController ios/littlehome/Externals/CTAssetsPickerController

and here's the error log

Debugger entered--Lisp error: (invalid-read-syntax "#")
signal(invalid-read-syntax ("#")) (condition-case errvar (progn (load savehist-file nil (not (interactive-p))) (setq savehist-loaded t)) (error (setq savehist-mode nil) (savehist-uninstall) (signal (car errvar) (cdr errvar)))) (progn (condition-case errvar (progn (load savehist-file nil (not (interactive-p))) (setq savehist-loaded t)) (error (setq savehist-mode nil) (savehist-uninstall) (signal (car errvar) (cdr errvar))))) (if (and (not savehist-loaded) (file-exists-p savehist-file)) (progn (condition-case errvar (progn (load savehist-file nil (not (interactive-p))) (setq savehist-loaded t)) (error (setq savehist-mode nil) (savehist-uninstall) (signal (car errvar) (cdr errvar)))))) (if (not savehist-mode) (savehist-uninstall) (if (and (not savehist-loaded) (file-exists-p savehist-file)) (progn (condition-case errvar (progn (load savehist-file nil (not (interactive-p))) (setq savehist-loaded t)) (error (setq savehist-mode nil) (savehist-uninstall)$
savehist-mode(1) eval-buffer(# nil "/home/eugenekim/.emacs.d/init.el" nil t) ; Reading at buffer position 19074
load-with-code-conversion("/home/eugenekim/.emacs.d/init.el" "/home/eugenekim/.emacs.d/init.el" t t)
load("/home/eugenekim/.emacs.d/init" t t) #[0 "^H\205\266^@ \306=\203^Q^@\307^H\310Q\202?^@ \311=\204^^^@\307^H\312Q\202?^@\313\307\314\315#\203*^@\316\202?^@\313\307\314\317#\203>^@\320\321\322!D\nB^R\323\202?^@\316\324^S\325^A\324\211#\210^K\324=\203e^@\326\327\330\307^H\331Q!\"\325^A\324\211#\210^K\324=\203$ command-line() normal-top-level()

This happens when I open multiple emacs -nw at the same time. Sometimes but not always.

  • Sometimes, I have the same problem with recentf.
    – djangoliv
    Nov 21 '16 at 10:11
  • To me the history file looks like it would be completely impossible to parse using load. Maybe it wasn't supposed to be saved in this format?
    – wvxvw
    Nov 21 '16 at 12:17
  • 1
    Unreadable elements should have been commented out by the code in savehist-save. Possibly magit should strip those properties when saving to kill-ring though.
    – npostavs
    Nov 21 '16 at 19:28
  • FWIW, I've long wished that the Emacs-Lisp reader would just skip any non-readable #(...) construct. Or that at least there would be a way (e.g. Boolean variable or wrapper macro) to let you make it do that. FWIW2, I just submitted Emacs bug #24982, to request such an enhancement.
    – Drew
    Nov 21 '16 at 21:50
  • @npostavs: Does it really do that ? I only see savehist-printable called for savehist-additional-variables. Anyway, I think save-hist should remove those properties.
    – politza
    Nov 22 '16 at 0:40

Lisp error: (invalid-read-syntax "#")

At the core it is obvious what is happening here.

  1. Something reads a string from a Magit buffer and does not remove properties.
  2. That string is given to completing-read, or similar, as INITIAL-INPUT, DEF, or a member of COLLECTION. completing-read does not remove the properties, you select the propertized string, and it is saved in a history variable.
  3. savehist, or similar, saves the value of that history variable without removing the properties.
  4. savehist tries to read the saved and unreadable value. It contains properties that form a recursive datastructure, and the reader fails at #1, which isn't valid read syntax.

Obviously something(tm) should remove the properties. I don't know who is considered to be responsible for doing it. It seems to me it would be a good idea to do it in (2). If (1) is responsible for doing it, then that is many different packages, and there will always be one who fails to do it.

Maybe it isn't done in (2) upfront for performance reasons. COLLECTION might be very large, and removing all properties from all members would be costly. But once the user has chosen a candidate doing it is very cheap, since its a single string. Infact completing-read does it, but ido-completing-read does not. You can observe that using emacs -Q. Evaluate each of these forms and select "bar". The first time you get a string with no properties, the second time you get one with properties.

(completing-read ": " (list "foo" (propertize "bar" 'face 'font-lock-error) "baz"))
(ido-completing-read ": " (list "foo" (propertize "bar" 'face 'font-lock-error) "baz"))

Ido then also stores that propertized string in the HIST variable. So this should probably be considered a bug in Ido.

I recommend you switch to Ivy, as I did a few weaks ago. But until a few weeks ago I did use Ido, and I did not have this issue then either, which means that for me (1) and/or (3) behaved differently than for you.

savehist does not contain code to remove properties. It does however contain code which checks whether the value can be read back and otherwise doesn't print it for good. Here is that code:

(defun savehist-save (&optional auto-save)
      (dolist (symbol savehist-additional-variables)
        (when (boundp symbol)
          (let ((value (symbol-value symbol)))
            (when (savehist-printable value)
              (prin1 `(setq ,symbol ',value) (current-buffer))
              (insert ?\n)))))

(defun savehist-printable (value)
  "Return non-nil if VALUE is printable."
   ;; Quick response for oft-encountered types known to be printable.
   ((numberp value))
   ((symbolp value))
   ;; String without properties
   ((and (stringp value)
         (equal-including-properties value (substring-no-properties value))))
    ;; For others, check explicitly.
      (condition-case nil
          (let ((print-readably t) (print-level nil))
          ;; Print the value into a buffer...
          (prin1 value (current-buffer))
          ;; ...and attempt to read it.
          (read (point-min-marker))
          ;; The attempt worked: the object is printable.
        ;; The attempt failed: the object is not printable.
        (error nil))))))

It appears to me that this code is actually doing what it is supposed to be doing, which makes me wonder whether it is actually run in your case. Maybe you have an old copy of savehist.el laying around on the load-path? Or you use have an extension to savehist installed which bypasses this code?

A final remark about (1). As I said, it is very likely that there are many packages out there that do not remove text properties every time the call completing-read (which may be redefined by things like Ido). But since completing-read does remove text properties I don't really think that should be considered a bug. I did use Ido for a long time, and there were some instances in Magit and my other packages where properties were not removed.

After I started using Ido I also started noticing these issues and fixed them. However I never had the issue you are having, probably because for me (3) did work as intended. The one instance where this did cause me issues was when I stored the propertized string in a sqlite database which eventually lead to "foreign key constraint failed". As you can imagine going from a database error to "somehow completing-read returns a propertized string" as the root cause was some detective work.

I don't think Magit is the one who messes up at (1). When Magit uses the section at point as the default completion candidate, it doesn't feed the completing function a string it extracts from the buffer, instead it uses the value of the section object. In the past there were some instances where that value was a propertized string. But as far as I remember that was all fixed and only involved branch names. The error you posted involves stashes and I had a brief look at all stash related completion and also did run some tests, just in case. But I never got any properties. So maybe it is an extension to Magit that does not remove the properties?

In summary: I don't think Magit passes propertizes strings, and even if it did that should not matter because completing-read removes them. ido-completing-read fails to do so, but again that should not result in the observed behavior, because savehist-save deals with that issue. Why that's not the case for you, I cannot figure out from here.

  • your guess was spot on. I had savehist.el lurking around. Thanks for the thought process!
    – eugene
    Jan 19 '17 at 11:55

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.