「Emacs」- (wrong-type-argument stringp (require . info))

问题描述

在执行 package-refresh-contents 命令时(或者其他命令),经常会出现如下几种错误:

...
Failed to download ‘melpa’ archive.
...
(wrong-type-argument stringp (require . china-util))
...
(wrong-type-argument stringp (require . info))
...

问题原因

# 09/12/2020

在使用 edebug 调试后,我们没有找到具体的原因。但是,有时候又能够正常更新,所以,我们猜测是网络原因导致响应内容被破坏而导致失败。

# 01/31/2021

在使用 mitmproxy 抓包后,我们发现响应内容没有问题。

# 03/03/2021

只要把 ~/.emcas 相关的配置移出(备份到其他目录,一切就恢复正常了)。我们又怀疑这个问题与 LC_CTYPE=zh_CN.UTF 环境变量有关。

# 03/04/2021

该错误会导致我们无法通过 Package 安装工具,这是最大的问题。但是,我们找到一个替代方案,来解决无法通过 Package 安装扩展的问题:

1)首先,不加载原有配置,而仅使用自己的配置,来启动:emacs –no-init-file –load init-lite.el

2)而配置文件 init-lite.el 仅包含用于安装扩展的配置:

(setq url-proxy-services
      '(("http" . "127.0.0.1:8123")
        ("https" . "127.0.0.1:8123")))

(custom-set-variables
 '(package-archives
   '(("gnu" . "https://elpa.gnu.org/packages/")
     ("melpa" . "https://melpa.org/packages/"))))

3)在安装完成之后,我们再重新启动 emacs 应用即可(应用的安装实际就是下载应用到 ~/.emacs.d/elpa/<package-name>/)。或者,在其他 Emacs 实例中执行:(package-initialize)

# 03/15/2021

在我们开启 debug on error(M-x toggle-debug-on-error [RET])后,再次执行引发错误的命令,我们得到如下信息:

Debugger entered--Lisp error: (wrong-type-argument stringp (require . china-util))
  string-match("\\(\\`\\|/\\)ffap\\(\\.elc\\|\\.el\\|\\.so\\)?\\(\\.gz\\)?\\'" (require . china-util))
  load-history-filename-element("\\(\\`\\|/\\)ffap\\(\\.elc\\|\\.el\\|\\.so\\)?\\(\\.gz\\)?\\'")
  eval-after-load("ffap" #f(compiled-function () #<bytecode 0xa15b69>))
  byte-code("\300\301\302\"\210\303\304\305\306\307DD\310\311\312\313\314&\7\210\303\315\305\306\316DD\317\311\312\313\314&\7\207" [eval-after-load "ffap" #f(compiled-function () #<bytecode 0xa15b69>) custom-declare-variable python-check-command funcall function #f(compiled-function () #<bytecode 0xa15715>) "Command used to check a Python file." :type string :group python python-check-buffer-name #f(compiled-function () #<bytecode 0xa15721>) "Buffer name used for check commands."] 8)
  python-mode()
  set-auto-mode-0(python-mode nil)
  set-auto-mode()
  normal-mode(t)
  after-find-file(nil t)
  find-file-noselect-1(#<buffer demo.py> "/tmp/demo.py" nil nil "/tmp/demo.py" (2097165 64768))
  find-file-noselect("/tmp/demo.py" nil nil t)
  find-file("/tmp/demo.py" t)
  funcall-interactively(find-file "/tmp/demo.py" t)
  call-interactively(find-file nil nil)
  command-execute(find-file)

查看 load-history-filename-element 函数,我们认为在 load-history 中(变量)包含非字符串类型元素,使用 C-h v load-history 查看该变量,其中确实包含非字符串类型变量:

...
Value:
(("/tmp/.mount_Emacs-Vf5AbQ/usr/share/emacs/27.1/lisp/cus-edit.elc"
  (require . cus-face)
  (require . wid-edit)
  (require . cus-load)
  (require . cus-start)
  custom-mode-map custom-mode-link-map custom-field-keymap
  (defun . custom-split-regexp-maybe)
...

解决方案

定义 load-history-filename-element 函数(添加类型检查),以覆盖原有定义:

;; 将如下定义,加入 ~/.emacs 或 ~/.emacs.d/init.el 文件
(defun load-history-filename-element (file-regexp)
  "Get the first elt of `load-history' whose car matches FILE-REGEXP.
        Return nil if there isn't one."
  (let* ((loads load-history)
         (load-elt (and loads (car loads))))
    (save-match-data
      (while (and loads
                  (or (null (car load-elt))
                      (not (and (stringp (car load-elt)) ; new condition
                                (string-match file-regexp (car load-elt))))))
        (setq loads (cdr loads)
              load-elt (and loads (car loads)))))
    load-elt))

参考文献

Emacs on Android – org-mode – error – `(wrong-type-argument stringp (require . t-mouse))`