常见故障排查
这是整套教程的急救手册:按”现象 / 原因 / 解决”三段式覆盖启动、包管理、显示、键位、性能、编码、网络与三大平台的典型问题,并给出可复制的排查命令与自己的排错记录方法。
一、排错方法论
1.1 五个基本工具
Emacs 的排错之所以让人畏惧,多数时候是因为不知道从哪儿看。记住下面五个工具,八成问题可以自己解决。
工具一:emacs -Q(最小复现)。 -Q 等价于 -q --no-site-file --no-splash,跳过所有用户与站点初始化文件。它的作用是回答一个最关键的问题:“这个故障是 Emacs 本身的问题,还是我的配置造成的?”
$ emacs -Q # 图形界面下的裸启动
$ emacs -Q --batch --eval '(princ (emacs-version))' # 无界面下的裸启动在 -Q 下故障消失,说明问题在你的配置里;故障依旧,说明问题在 Emacs 构建、系统环境或外部程序。
工具二:emacs --debug-init。 加载初始化文件出错时直接进入 Lisp 调试器并显示 backtrace。读 backtrace 的方法是先看最内层的几帧(真正出错的地方),再往上看是哪个函数调用了它。如果最内层是你的配置里的某个函数,问题就在那里;如果最内层是内置函数,看它的参数是否合理。
工具三:*Messages* 缓冲区与 M-x view-lossage。 前者是 Emacs 的消息日志,很多”没有报错但行为不对”的问题在这里能找到线索(例如某个包静默失败时会留一行消息)。后者显示最近输入的按键,用于排查”我按的键到底被谁吃掉了”。
C-h e 打开 *Messages* 缓冲区
M-x view-lossage 显示最近 300 个输入事件工具四:M-x toggle-debug-on-error。 打开之后,任何 Lisp 错误都会进入调试器而不是只打印一行消息。排查”某个命令执行一半就停了”的问题时,它比反复读代码有效得多。相关命令还有 M-x toggle-debug-on-quit(C-g 时进入调试器,用于定位”卡住”的位置)与 M-x debug-on-entry(在指定函数入口处中断)。
工具五:二分注释法。 把配置分成两半,注释掉一半,看故障是否消失。它慢,但它的结论不依赖任何假设。对于大型配置,这是最可靠的定位手段。
1.2 排错决策流程图
flowchart TD A["出现故障"] --> B{"能用 emacs -Q 复现吗"} B -->|"能"| C["问题在 Emacs 构建或系统环境"] B -->|"不能"| D{"--debug-init 有 backtrace 吗"} D -->|"有"| E["按最内层帧定位到具体配置行"] D -->|"没有"| F{"*Messages* 里有线索吗"} F -->|"有"| G["按消息内容搜索或查阅手册"] F -->|"没有"| H["用二分法注释配置定位"] C --> I["检查外部程序、字体、编码、权限"] E --> J["修复并复测"] G --> J H --> J I --> J J --> K{"解决了吗"} K -->|"否"| H K -->|"是"| L["记录到自己的排错笔记"]
1.3 问题分类结构图
本篇后面的十节按下面的分类组织。先确定故障属于哪一类,再跳到对应的小节,比从头往下读快得多。
graph TD A["故障现象"] --> B["启动类:起不来或起来就报错"] A --> C["包管理类:装不上或装完不对"] A --> D["显示类:字体、图标、主题"] A --> E["键位类:按键没反应或被占用"] A --> F["性能类:慢、卡、占资源"] A --> G["文件与编码类:乱码、换行、权限"] A --> H["网络类:超时、代理、证书"] A --> I["Windows 特有:HOME、PATH、shell"] A --> J["macOS 特有:PATH、修饰键、门禁"] A --> K["服务器与无界面:daemon、终端限制"] B --> L["先看 backtrace 再二分配置"] C --> M["先查仓库与签名再查缓存"] D --> N["先用 C-u C-x = 看实际字体"] E --> O["先用 C-h k 看实际绑定"] F --> P["先用 profiler 采样再动手改"] G --> Q["先用 C-x RET r 换编码重读"] H --> R["先在终端用 curl 验证连通性"]
1.4 二分法的正确操作
不要一行一行地试,要按模块切分:
;; 第一轮:注释掉后半部分,看故障是否消失
;; (require 'my-prog)
;; (require 'my-org)
;; (require 'my-git)
;; 第二轮:如果消失了,说明问题在后半部分;把后半部分再对半分
;; (require 'my-prog)
;; 第三轮:范围缩小到一个文件之后,再按 top-level 形式对半分对单个文件内的错误,先 M-x check-parens 检查括号是否配平,再用 M-x up-list(或 C-M-u)逐层确认结构。
二、启动类问题
| 现象 | 原因 | 解决 |
|---|---|---|
启动时报 End of file during parsing 或 Invalid read syntax | init.el 括号不配平或字符串未闭合 | 打开 init.el 执行 M-x check-parens;用 C-M-u 逐层上跳确认;用 M-x show-paren-mode 打开括号配对高亮 |
| 启动直接闪退、看不到任何信息 | 错误发生在早期,图形窗口还没建立;或者错误发生在 early-init.el | 用 emacs --debug-init 从终端启动,错误会打印在终端里;或 emacs -Q --batch -l ~/.emacs.d/init.el 强制在批处理下暴露错误 |
--debug-init 输出很长看不懂 | backtrace 是从外到内排列的调用栈,最内层才是出错点 | 从 Debugger entered--Lisp error: 下面第一行开始读;eval-buffer / load / require 这些帧只表示”从这里进入”,真正的问题在其下方 |
提示 Cannot open load file: No such file or directory, xxx | require 的包没装,或 load-path 里没有对应目录 | 用 M-x locate-library RET 包名 RET 确认能否找到;检查 load-path 是否包含你的私有模块目录;检查包是否真的安装成功 |
| Emacs 升级后配置大量报错 | 新版本移除了旧 API,或内置包的行为改变 | 先看每条错误的函数名,用 C-h f 确认它是否还存在;旧式 cl 库已被 cl-lib 取代,检查 require 'cl 的地方改为 cl-lib 并给函数加 cl- 前缀 |
| 原生编译相关的报错出现在启动时 | 缺少 libgccjit、或缓存与版本不匹配 | 用 (native-comp-available-p) 判断支持情况;确认 comp-native-version-dir 与当前 Emacs 版本一致;必要时删除旧的 eln 缓存 |
early-init.el 里的错误更难定位 | 它在图形界面与包系统之前执行,出错时没有任何缓冲 | 临时在 early-init.el 第一行插入 (setq debug-on-error t);或把它整个改名,确认”错误是否确实来自这里” |
启动时弹出 *Warnings* 缓冲区 | 某个包在编译或加载时产生警告 | 看警告内容所属文件;native-comp-async-report-warnings-errors 为 'silent 或 nil 可以抑制异步编译警告(见 [[emacs教程/7进阶/02_NativeComp与字节码 |
启动后停在 Loading ... 不动 | 加载某个文件时卡住:网络请求、等待输入、死循环 | 按 C-g 中断,看 *Messages* 最后一行是哪个文件;常见原因是启动时的包检查联网超时 |
启动报 Symbol's value as variable is void | 使用了尚未定义的变量 | 用 boundp 保护版本相关变量;检查是否漏写 defvar 或漏 require 定义它的库 |
| 配置文件被当成只读、无法保存 | 文件属主或权限被改(例如曾用 sudo emacs 编辑过) | ls -l 查看属主,用 sudo chown $USER 文件 修正;永远不要用 sudo 启动整个 Emacs |
2.1 读一份真实的 backtrace
Debugger entered--Lisp error: (void-function my-nonexistent-helper)
my-broken-setup()
run-hooks(prog-mode-hook)
set-auto-mode-0(prog-mode nil)
...读法:最上面一行是”到底出了什么错”(调用了不存在的函数 my-nonexistent-helper),第二行是”谁调用了它”(你的 my-broken-setup),再往下是触发路径(进入 prog-mode 时运行钩子)。修复目标是第二行那个函数体。
三、包管理类问题
| 现象 | 原因 | 解决 |
|---|---|---|
安装包时提示签名验证失败(Failed to verify signature) | GNU ELPA 的签名密钥过期或你的 keyring 太旧 | 安装 GNU ELPA 上的 gnu-elpa-keyring-update 包:M-x package-install RET gnu-elpa-keyring-update RET;如果因为签名失败装不上,先从 https://elpa.gnu.org/packages/gnu-elpa-keyring-update.html 手动下载安装,再执行一次更新 |
| 想知道签名验证当前处于什么状态 | 新版 Emacs 里 package-check-signature 既是变量也是函数 | 用 C-h v package-check-signature 看设置,用 C-h f package-check-signature 看实际生效值;本机 Emacs 31.1 上的默认值是 allow-unsigned |
M-x package-refresh-contents 一直超时 | 网络不通、被墙、或需要代理 | 用 curl -I https://melpa.org/packages/archive-contents 在终端验证;需要代理时设置 url-proxy-services(见第八节) |
| 下载极慢或总失败 | 直连 GNU 与 MELPA 的网络质量差 | 换镜像源:把 package-archives 指向可用的镜像;注意镜像的同步延迟与完整性 |
| 装了包但命令找不到 | 包只装了一半、autoload 文件没生成、或 package-quickstart 未刷新 | M-x package-quickstart-refresh;重启 Emacs;确认包的目录在 package-user-dir 下 |
use-package 的 :ensure 装不上 | 该包不在已配置的仓库里,或需要 use-package-always-ensure 配合 | 用 M-x list-packages 搜索确认包名与所在仓库;MELPA 的包需要先在 package-archives 里加入 MELPA |
| 升级某个包之后配置报错 | 包的 API 变更(函数改名、参数变化、行为不同) | 用 C-h f 对照新版本的文档字符串;查看包的 NEWS 或提交记录;必要时用 M-x package-install 装回旧版本(需要指定版本时用 M-x package-vc-install 或手工下载) |
| 包目录损坏(文件缺失、解压不完整) | 下载中断、磁盘问题、手工删除过文件 | 用 M-x package-delete 卸载再重装;严重时删除 ~/.emacs.d/elpa/archives/ 后重新 M-x package-refresh-contents |
误删了 elpa 目录下的包 | 手工清理时删多了 | 重新 M-x package-refresh-contents,然后用 M-x package-install 逐个装回;用 use-package 的 :ensure 声明可以让它自动补装 |
| 同时存在两个同名包冲突 | load-path 里有多份同名库(例如手工 clone 的一份与包管理器装的一份) | M-x locate-library 看实际加载的是哪一份;清理 load-path 中的重复目录 |
| 依赖冲突导致某个包无法启用 | 两个包要求同一依赖的不同版本 | 用 M-x package-list-packages 查看依赖关系;优先卸载不再维护的一方 |
package-quickstart.el 里的内容过时 | 装包或升级后没有刷新 | M-x package-quickstart-refresh;不要手工编辑该文件 |
四、显示与字体类问题
| 现象 | 原因 | 解决 |
|---|---|---|
| 中文显示成方块(豆腐块) | 字体里没有中文字形,或 fontset 没有配置 | 用 C-u C-x =(M-x what-cursor-position 带前缀)查看当前字符用的是哪个字体;用 set-fontset-font 指定中文字体,例如把 “Noto Sans CJK SC” 或 “Source Han Sans SC” 配给 han 字符集 |
| 字体设置不生效 | 设置的是 default face 的字体,但当前 frame 已有独立设置;或字体名写错 | 用 M-x describe-face RET default RET 查看实际生效值;字体名必须与系统里的实际名称完全一致,用 fc-list 或系统字体管理器确认 |
| 图标显示为方框 | 缺少图标字体(nerd-icons 需要 Nerd Fonts,all-the-icons 需要 All-the-icons 字体) | 安装对应字体后执行 M-x nerd-icons-install-fonts 或 M-x all-the-icons-install-fonts,然后重启 Emacs 并清空字体缓存(M-x clear-font-cache 或删除系统字体缓存) |
| emoji 显示不正常 | 缺少彩色 emoji 字体 | 安装 Noto Color Emoji 一类字体;在图形环境下把它配到 emoji 字符集;终端下的支持取决于终端本身 |
| 主题加载失败,颜色不对 | 主题文件不完整、加载顺序被后续设置覆盖、或终端不支持对应颜色数 | M-x load-theme RET 主题名 RET t 看是否报错;用 M-x disable-theme 逐个排除;确认没有在自己的配置里硬编码 face 颜色覆盖主题 |
| 找不到想用的主题 | 主题不在已配置的仓库,或没有安装 | M-x package-install RET 主题包名 RET;modus-themes 与 ef-themes 在 GNU ELPA 上,doom-themes 与 catppuccin-theme 在 MELPA 上 |
| HiDPI 屏幕上字太小或模糊 | 缩放没有传递到 Emacs | 图形环境下用 M-x text-scale-increase 临时缩放;要全局调整则可通过 frame 的字体设置或启动参数;工具包框架(如 fontaine)可以按 frame 设置字体 |
| 终端下颜色只有 8 色 | TERM 设置不当或终端不支持 256 色 | 确认 TERM 是 xterm-256color 一类;用 M-x list-colors-display 看可用颜色数量 |
| Windows 上窗口分隔线看不见 | 默认的分隔线在 Windows 上对比度不足 | 显式设置 window-divider 相关 face 或开启 window-divider-mode;本机主题默认值可能不适合 |
| macOS 全屏后内容被刘海遮挡 | 全屏模式下 frame 铺满屏幕 | 用非全屏的最大化替代全屏;或调整 frame 的 ns- 系列参数(用 C-h v ns- TAB 查看可用项) |
| 光标在终端里看不见 | 终端的光标形状与颜色被覆盖 | M-x blink-cursor-mode 切换;终端模拟器自身的设置优先 |
4.1 中文字体的可复用配置
;; 图形环境下为中文字符集指定字体;名字必须与系统里实际存在的字体一致
(when (display-graphic-p)
(dolist (charset '(han cjk-misc bopomofo))
(set-fontset-font t charset
(font-spec :family "Noto Sans CJK SC")))
;; emoji 单独指定,避免被中文字体抢走
(set-fontset-font t 'emoji
(font-spec :family "Noto Color Emoji")))# 确认系统里到底装了哪些字体(Debian/Ubuntu 与 Arch 需要 fontconfig)
$ fc-list :lang=zh family | sort -u | headmacOS 与 Windows 的字体名不同:macOS 常见 “PingFang SC”、“Hiragino Sans GB”;Windows 常见 “Microsoft YaHei”、“SimSun”。用 fc-list(Windows 上需要先装 fontconfig)或系统字体管理器确认。
五、键位类问题
| 现象 | 原因 | 解决 |
|---|---|---|
| 按了某个键没有任何反应 | 该键在当前 major mode 或 minor mode 里未绑定 | C-h k 按键 看它当前绑到了哪个命令;C-h b 看当前所有绑定;M-x where-is RET 命令名 RET 反查命令绑在哪个键上 |
| 自己设置的键位被覆盖 | major mode 的键位表优先级高于全局表;或某个 minor mode 抢先绑定 | 把键位挂到对应 mode 的 hook 里用 local-set-key 设置;或用 general 一类工具明确指定优先级 |
| 键位时灵时不灵 | 某个 minor mode 只在特定缓冲区启用 | C-h k 能看出实际生效的是哪个 minor mode 的绑定;用 M-x describe-mode 查看当前启用的所有次要模式 |
终端里 C-/ 与 C-_ 无效 | 终端把这两个键序列映射成了别的东西,或根本不发送 | 改用 C-x u 或 M-x undo;也可以绑定一个确定能传到的键,例如 C-c u |
| 输入法冲突,中文输入被 Emacs 拦截 | 系统输入法与 Emacs 输入法(M-x set-input-method)同时生效 | 只保留一个:用系统输入法时,不要启用 Emacs 的 toggle-input-method;反之亦然 |
C-g 无效,Emacs 一直卡住 | 卡在原生代码或外部进程等待中,Lisp 层没有机会检查 quit 标志 | 先用 M-x toggle-debug-on-quit 定位是否在 Lisp 层;涉及外部进程时从终端 kill 那个进程;频繁出现说明某个包有 bug |
which-key 不显示 | 未启用,或与被延迟加载的键位表冲突 | Emacs 30 起 which-key 内置,用 M-x which-key-mode 启用;确认它没有被延迟到键位表建立之后才加载 |
evil 状态异常(在插入模式下按 ESC 没回到普通模式) | evil 的初始化顺序与模式钩子冲突 | 确认 evil-mode 在配置早期启用;用 C-h v evil-state 查看当前状态;检查是否有包拦截了 ESC |
| 自定义键位丢失 | define-key 作用的 keymap 当时还不存在,或作用后被包重建 | 用 with-eval-after-load 包住,或让 use-package 的 :bind 处理延迟加载 |
| 前缀键被当成命令执行 | 前缀键超时(C-c 之类不会超时,但有些自建前缀会) | 检查是否设置了 echo-keystrokes 之外的超时相关变量;确认前缀键没有被局部表覆盖 |
5.1 键位排查的固定顺序
1. C-h k 按键 当前绑的是什么(含生效的 mode)
2. C-h b 当前缓冲区的完整键位表
3. M-x describe-mode 当前启用的 major 与 minor mode 列表
4. M-x where-is 命令 命令有没有被绑到键上
5. M-x view-lossage 最近按的键是否真的送达了 Emacs第 5 步经常被跳过,但它是”按键到底有没有到 Emacs”的唯一直接证据。终端复用器(tmux、screen)与终端模拟器都可能截获按键。
六、性能类问题
| 现象 | 原因 | 解决 |
|---|---|---|
| 启动慢 | 重包在启动时全部加载、GC 频繁、联网检查超时 | 见 [[emacs教程/7进阶/01_启动加速与性能优化 |
| 输入卡顿 | 语法分析、行内 overlay、补全源查询、输入法交互 | 用 M-x profiler-start RET cpu RET 采样后 M-x profiler-report;怀疑 overlay 时求值 (length (overlays-in (point-min) (point-max))) |
| 感觉 GC 频繁 | 默认 GC 阈值较小,或某个操作产生大量短命对象 | 观察 gcs-done 与 gc-elapsed 的增长速度;用空闲定时器打印它们的差值,确认是否真有频繁回收 |
| 打开大文件就卡 | 文件行数多或存在超长行;字体锁定、行号、折行叠加 | M-x so-long-mode 或启用 global-so-long-mode;用 M-x find-file-literally 以不转换的方式打开;日志文件用 fundamental-mode 打开 |
| TRAMP 路径上操作卡死 | 每次文件操作一次远程调用;目录遍历类工具在远程放大成本 | 见 [[emacs教程/7进阶/03_TRAMP远程开发 |
| LSP 占满 CPU | 项目太大、服务器在重新索引、或客户端配置触发了全量重解析 | M-x list-processes 找到语言服务器进程;M-x eglot-shutdown 或对应客户端的关闭命令停掉;检查项目是否把构建产物纳入了索引范围 |
magit-status 慢 | 大型仓库的文件状态查询量巨大 | 在远程目录上尤其明显;用 magit-todos 一类附加功能时注意它们的额外扫描;必要时在终端里用 git status |
| 空闲时 CPU 仍然很高 | 定时器任务、文件监视(auto-revert-mode)、后台编译 | M-x profiler-start 采样一段时间;M-x list-timers 查看活动定时器;M-x list-processes 查看后台进程 |
| 内存占用持续增长 | GC 阈值被永久调大、某个缓冲区持有大量数据、缓存无上限 | 检查是否忘记恢复 GC 阈值;用 M-x memory-report 查看各缓冲区与变量的内存占用 |
| 光标移动一顿一顿 | 行号显示(尤其是相对行号)、折行、长行 | 关掉相对行号;对长行文件关闭 truncate-lines 之外的高亮功能 |
6.1 观察 GC 是否真的频繁
;; 记录起点
(defvar my/gc-watch-start (list gcs-done gc-elapsed))
(defun my/gc-watch-report ()
"报告自起点以来的 GC 次数与耗时。"
(interactive)
(message "GC 次数增加 %d 次,耗时增加 %.3f 秒"
(- gcs-done (car my/gc-watch-start))
(- gc-elapsed (cadr my/gc-watch-start))))
;; 正常编辑一分钟后执行 M-x my-gc-watch-report
;; 次数增加几十次以上,才说明 GC 是主要瓶颈七、文件与编码类问题
| 现象 | 原因 | 解决 |
|---|---|---|
| 文件打开是乱码 | 编码探测错误(常见于 GBK/GB18030 文件) | C-x RET r(M-x revert-buffer-with-coding-system)用指定编码重新读取,例如选 chinese-gbk;确认无误后用 C-x RET f(M-x set-buffer-file-coding-system)指定保存编码 |
| 保存后文件换行符不对 | 文件原本是 CRLF,被以 LF 保存(或反之) | 打开时看模式行的编码提示;用 C-x RET f 指定 unix(LF)或 dos(CRLF);批量转换可以在终端用 dos2unix / unix2dos |
| 保存时提示文件已被外部修改 | 文件被别的程序(或另一个 Emacs 实例)改过 | 按提示选择是否覆盖;用 M-x revert-buffer 重新读取;想比较差异时先 C-x C-w 另存一份 |
| 提示权限不足无法保存 | 文件属主是 root 或只读 | 用 /sudo:: 前缀重新打开(见 [[emacs教程/7进阶/03_TRAMP远程开发 |
| 路径含中文或空格时出错 | 外部程序对路径的处理差异,或 shell 引用问题 | 尽量避开;必须使用时确认调用外部命令的地方做了正确引用;TRAMP 路径含空格时尤其容易出问题 |
目录里堆满 #foo# 与 foo~ | 自动保存文件与备份文件 | #foo# 是自动保存文件,foo~ 是备份文件;用 M-x customize-group RET backup RET 与 auto-save 组调整策略;也可把备份集中到一个目录 |
| undo 历史丢失 | 缓冲区被杀死、revert-buffer 重建、或 undo 边界被设置 | C-/ 回退到边界后可以再用 C-/ 越过边界(Emacs 会询问);undo-tree 或 vundo 提供可视化的撤销树 |
打开文件提示 File is large | 超过 large-file-warning-threshold | 确认是否真要在 Emacs 里打开;处理日志用外部工具;需要调阈值时改这个变量 |
| 文件末尾换行被自动补上或删掉 | require-final-newline 的设置 | C-h v require-final-newline 查看当前值并按需调整;用 M-x find-file-literally 打开可完全避免自动处理 |
| 文件名大小写敏感问题 | 在大小写不敏感的文件系统(Windows、macOS 默认)上编辑 | 避免创建仅大小写不同的同名文件;Git 里尤其容易出问题(用 git mv 强制改名) |
八、网络类问题
| 现象 | 原因 | 解决 |
|---|---|---|
| 包仓库访问超时 | 需要代理,或网络策略限制 | 在 Emacs 里设置 url-proxy-services;或设置 http_proxy 与 https_proxy 环境变量后从同一 shell 启动 Emacs |
| 终端里能用代理,桌面图标启动就不行 | 图形方式启动的 Emacs 读不到 shell 的环境变量 | 用 exec-path-from-shell(macOS 与图形 Linux 常用)导入环境变量;或者把代理配置直接写进 Emacs 配置 |
提示证书错误(Certificate could not be verified) | 系统证书库不全,或公司网络做了中间人代理 | 安装系统 CA 证书包;公司环境的自签证书需要导入系统信任库,Emacs 走系统库才能识别 |
| TRAMP 连不上或极慢 | ssh 配置、跳板机、防火墙、连接复用未生效 | 先在终端验证 ssh 主机;参考 [[emacs教程/7进阶/03_TRAMP远程开发 |
| 下载包时偶发失败但重试就好 | 网络抖动或镜像同步中 | 重试;把镜像换成更稳定的源;避免在弱网时批量升级所有包 |
;; 让 Emacs 的 url 库走本地代理
(setq url-proxy-services
'(("http" . "127.0.0.1:7890")
("https" . "127.0.0.1:7890")))
;; 端口与协议按你自己的代理软件实际配置填写# 验证代理与仓库可达性
$ curl -x http://127.0.0.1:7890 -I https://melpa.org/packages/archive-contents九、Windows 特有问题
| 现象 | 原因 | 解决 |
|---|---|---|
| Emacs 找不到配置文件 | HOME 环境变量未设置,配置落到了别处 | 在系统环境变量里设置 HOME(例如 C:\Users\你的用户名);配置放在 %APPDATA%\.emacs.d\ 或 HOME%\.emacs.d\;用 C-h v user-emacs-directory 确认实际路径 |
| 提示找不到某个外部命令 | exec-path 与系统 PATH 不同步 | 用 C-h v exec-path 与 C-h v PATH 对比;在配置里把需要的目录加入 exec-path;executable-find 可以验证某个程序能否被找到 |
M-x shell 的行为和 Linux 差很多 | 默认使用 cmd.exe,不是 bash | C-h v shell-file-name 查看当前 shell;可用 Git for Windows 自带的 bash 或 PowerShell(用 explicit-shell-file-name 设置) |
| 目录符号链接创建失败 | Windows 创建符号链接需要管理员权限或开发者模式 | 用目录联接(junction)代替:cmd /c mklink /J 链接 目标;或开启开发者模式 |
| 从资源管理器双击文件不能在已运行的 Emacs 里打开 | 需要配置 emacsclientw 与文件关联 | 用 emacsclientw.exe 作为关联程序,并确保 Emacs 服务器已启动((server-start));emacsclientw 是无需控制台窗口的变体 |
| 字体与 DPI 显示不理想 | 高 DPI 屏幕上的缩放设置 | 调整系统缩放;在 Emacs 里通过 frame 字体与 text-scale 微调 |
| 路径太长导致失败 | Windows 的路径长度历史限制 | 把工作目录移到更浅的位置;启用系统对长路径的支持;避免深层嵌套的依赖目录 |
文本文件出现 ^M | 文件是 CRLF 换行而 Emacs 按 LF 处理 | C-x RET f 指定 dos 编码;或用 .gitattributes 统一换行符 |
| 终端里按键行为异常 | Windows 终端对部分组合键的映射不同 | 用 C-h k 确认按键是否送达;必要时在配置里改用其他键位 |
find 或 grep 类功能报错 | 缺少 GNU 工具链 | 安装 Git for Windows 或 MSYS2 提供的工具,并把它们的 bin 目录加入 exec-path |
十、macOS 特有问题
| 现象 | 原因 | 解决 |
|---|---|---|
从图形界面启动时读不到 shell 里的 PATH | macOS 的图形程序不继承登录 shell 的环境 | 用 exec-path-from-shell:M-x package-install RET exec-path-from-shell RET 后在配置里调用 exec-path-from-shell-initialize |
Command 键不是 Meta | macOS 上 Command 默认被映射为 Super,Option 才是 Meta | 用 M-x customize-variable RET mac-command-modifier RET 把 Command 设为 meta;或熟悉 Option 作为 Meta 的默认布局 |
| 输入法切换行为与其他应用不同 | Emacs 的输入法机制与系统输入法的交互 | 决定只用一套:用系统输入法时不要在 Emacs 里启用 set-input-method;反之用 C-\ 切换 Emacs 输入法 |
| 全屏后出现异常(内容被遮挡或 frame 尺寸不对) | 刘海屏与全屏 frame 参数 | 用最大化替代全屏;用 C-h v ns- TAB 查看可调的 macOS 专用参数 |
| 首次运行被 Gatekeeper 阻止 | 未签名或未公证的程序 | 在”系统设置 - 隐私与安全性”里允许;或用 Homebrew 安装的版本(已经处理过签名) |
| 字体渲染与 Linux 差异明显 | macOS 的字体渲染与度量不同 | 调整字体族与字号;对中英文混排分别指定字体 |
| 使用 Homebrew 安装后命令名冲突 | 系统自带的旧 Emacs 与 Homebrew 版本同时存在 | which -a emacs 查看所有可执行文件;调整 PATH 顺序;必要时用绝对路径启动 |
十一、服务器与无 GUI 环境
| 现象 | 原因 | 解决 |
|---|---|---|
| 终端界面下功能受限 | 没有图形 frame,图片、部分字体效果、GUI 特性不可用 | 属于设计限制;需要图形能力时用 X11 转发或远程 daemon 加图形客户端 |
| 没有 X 时启动报错 | 尝试创建图形 frame | 用 emacs -nw(等价于 --no-window-system)或在配置里判断 (display-graphic-p) 后再设置图形相关项 |
| 守护进程启动后连不上 | socket 目录权限、XDG_RUNTIME_DIR 未设置、或服务未启用 | C-h v server-socket-dir 查看 socket 位置;journalctl --user -u emacs -n 50 看日志;确认 systemctl --user enable --now emacs 成功 |
| 断开 ssh 后 daemon 也没了 | systemd 用户服务随会话结束而停止 | sudo loginctl enable-linger 用户名 让用户服务常驻 |
| 终端下看不到图标与美化字符 | 终端字体不含对应字形 | 安装 Nerd Fonts 并配置终端使用它;或去掉依赖图标的包 |
| daemon 里的配置与终端里的行为不同 | daemon 启动时没有终端环境 | 在配置里用 (daemonp) 判断是否运行在守护进程下,区分设置 |
| 服务器上缺少编译依赖导致功能缺失 | 发行版的最小化安装 | 按需安装 libgccjit、字体、GnuPG 等;用 (native-comp-available-p) 等方法确认能力 |
;; 判断是否运行在 daemon 下
(when (daemonp)
;; 守护进程里不适合做图形相关设置
(setq inhibit-startup-screen t))
;; 判断是否有图形显示能力
(when (display-graphic-p)
;; 只有在图形环境下才设置字体、图标、工具栏
)十二、如何提问与反馈 bug
12.1 M-x report-emacs-bug
这是向 GNU 报告 Emacs 自身问题的正确入口。它会打开一个邮件缓冲区,自动填入版本号、系统信息、配置相关的诊断内容,你只需要补充”怎么复现”。
写好一份报告的关键是用它自带的信息做基础,再补上三样东西:
- 精确的复现步骤:从
emacs -Q开始,逐条列出输入与按键,直到问题出现。 - 预期行为与实际行为:各写一句,不要只写”不好用”。
- backtrace 或错误消息:用
--debug-init或M-x toggle-debug-on-error取得。
发送方式:在报告缓冲区里按 C-c C-c(具体键位以缓冲区里的提示与 C-h m 为准)。如果暂时发不出去,可以把缓冲区内容保存下来,稍后从任意邮件客户端发出。
12.2 到社区提问
到 Emacs 中文社区或 Stack Exchange 一类站点提问时,最小复现配置是通行证。写法是把问题从 emacs -Q 开始逐步加回来,直到问题复现,然后把这段最小代码贴出来:
;; 最小复现配置:从 emacs -Q 启动,只加载下面这些内容
;; 1. 已确认问题与我的完整配置无关(emacs -Q 下也复现 / 只在加上这一行后复现)
(require 'some-package)
(setq some-variable t)
(add-hook 'prog-mode-hook #'some-mode)
;; 2. 复现步骤:
;; emacs -Q -l /tmp/minimal.el 打开 test.py
;; 然后按 C-c C-x
;; 3. 实际行为:报错 void-function some-helper
;; 4. 预期行为:应当插入一行注释提问时提供的信息清单:
- Emacs 版本(
M-x emacs-version,或用(emacs-version))与获取方式(发行版包 / Homebrew / 自编译)。 - 操作系统与版本。
- 相关包的版本(用
M-x list-packages查看,或C-h v看包内的版本变量)。 - 最小复现配置与完整错误信息。
- 已经尝试过哪些排查手段(这能省下大量来回确认的时间)。
不要做的事情:只贴一张截图、只说”不工作”、把整个两千行 init.el 贴出来让别人找。这些提问方式既不尊重别人的时间,也很难得到有效回答。教程资源与社区入口见 学习资源与视频。
十三、建立自己的排错记录
同一个问题你一定会遇到第二次。把每次解决过程写进一个 org 文件,几个月后它就是最贴合你自己环境的资料库。
* 启动时 TRAMP 失效
:PROPERTIES:
:日期: 2026-01-12
:标签: tramp 启动 性能
:END:
** 现象
优化启动后,打开 /ssh: 路径时提示 Not a Tramp file name。
** 原因
early-init.el 里把 file-name-handler-alist 置为 nil,恢复语句写在了
一个 when 分支里,该分支在终端环境下不成立。
** 解决
把恢复改成无条件的命名函数,挂到 emacs-startup-hook。
验证:(assq 'tramp-autoload-file-name-handler file-name-handler-alist)
** 复发的判断方法
只要 C-x C-f 输入 /ssh: 前缀不再被识别,先看这个变量。
** 相关
见教程 7进阶/01 第四节。
* 中文文件在终端里显示为问号
:PROPERTIES:
:日期: 2026-02-03
:标签: 字体 终端
:END:
** 现象
** 原因
** 解决
** 复发的判断方法模板的五个小节缺一不可,尤其是**“复发的判断方法”**:它把你从”记得这个坑”变成”能在一分钟内确认是不是同一个坑”。用 M-x org-capture 配一个模板,记录成本可以降到十几秒。
十四、三分钟应急流程
Emacs 跑不起来时,按顺序做这五步,通常能在三分钟内定位方向。
第一步:从终端启动,看输出。
$ emacs --debug-init图形窗口闪退时,这是唯一能看到错误的方式。
第二步:用 -Q 判断是不是配置的锅。
$ emacs -Q-Q 下正常、正常启动出错,问题在配置;-Q 下也出错,问题在环境或 Emacs 本身。
第三步:确认配置文件是否可读、括号是否配平。
$ ls -l ~/.emacs.d/init.el ~/.emacs.d/early-init.el
$ emacs -Q --batch -l ~/.emacs.d/init.el 2>&1 | head -20第三步能同时暴露”文件属主被 sudo 改过”与”括号不配平”两类高频问题。
第四步:读 backtrace 的最内层帧。
Debugger entered--Lisp error: (...)
← 第一行是错误本身
← 第二行是出错的那个函数把那个函数名在你配置里搜一遍,问题基本就定位了。
第五步:找不到就二分。
注释掉一半配置,重启,看故障是否消失。重复。二分法不需要理解故障原因,它只需要你耐心。
小结
- 排错的固定顺序是:
emacs -Q判断范围、--debug-init拿 backtrace、看*Messages*、必要时二分;先确定”问题在哪一层”,再谈修复。 - 大多数”疑难杂症”属于四类:版本差异(API 变了)、平台差异(Windows 的 HOME、macOS 的 PATH)、配置顺序(延迟加载与钩子的时机)、以及优化带来的副作用(忘记恢复的全局变量)。
- 每次解决完都写进自己的 org 排错记录,并留下”复发的判断方法”,你的排错速度会随使用年限线性提高。
相关章节
参考
- GNU Emacs 手册(问题排查、编码系统、Windows 与 macOS 专用章节)https://www.gnu.org/software/emacs/manual/html_node/emacs/
- Elisp 参考手册(调试器与错误处理的权威说明)https://www.gnu.org/software/emacs/manual/html_node/elisp/
- Emacs Wiki(历史遗留下来的疑难杂症合集)https://www.emacswiki.org/
- GNU ELPA 上的
gnu-elpa-keyring-update包页面 https://elpa.gnu.org/packages/gnu-elpa-keyring-update.html - Emacs 中文社区论坛(中文环境下的字体、输入法、Windows 问题经验最集中)https://emacs-china.org/
- Mastering Emacs 博客(故障与配置专题)https://www.masteringemacs.org/
- Sacha Chua 周报(社区里反复出现的坑与解法)https://sachachua.com/blog/