air启动失败主因是配置错误而非工具故障:需确保root指向含go.mod的根目录、build.bin与build.cmd路径严格一致、include_ext在[watch]和[build]两节均正确配置。

Go 本身不支持热重载,所有可用方案都是“进程级重启”——即杀掉旧进程、编译新二进制、拉起新进程。这不是缺陷,而是设计使然;关键在于选对工具、配对路径、避开权限和监听盲区。
air 启动失败:先看 go build 能不能过
报 failed to build, error: exit status 2,90% 是 Go 代码本身编译失败,不是 air 配置问题。别急着改 .air.toml,直接在终端执行 go build,看是否出现 undefined: xxx 或 import cycle not allowed。只有确认代码能编译通过,再让 air 接管才合理。
常见干扰项:
-
go.mod不在当前目录:air 默认从含go.mod的目录启动,否则找不到模块根路径 - 编辑器保存时文件未落盘:
delay = 1000别乱设成 200,否则可能构建时读到半截文件 - 用了
//go:build约束但没同步更新构建命令,导致 air 编译时忽略关键文件
修改 .yaml 或 .html 不触发重启?两处 include_ext 必须都配
air 默认只监听 .go 文件。想让 config.yaml 或模板变更也触发构建,必须在 .air.toml 的两个位置同时加后缀:
[watch] 节里写 include_ext = ["go", "yaml", "yml", "html", "tmpl"];[build] 节里也要写同样的 include_ext,否则 air 会跳过这些文件的变更检测。
注意代价:
- Linux 下 inotify 句柄数有限(默认 8192),加太多后缀容易触发
inotify limit reached - 改静态资源建议用程序内监听(如
fsnotify加载模板),而非依赖 air 杀进程——避免 HTTP 连接中断、状态丢失
fork/exec ./tmp/main: permission denied:Linux/macOS 权限与路径一致性问题
这是 macOS 和 Linux 上最典型的执行失败,根源是生成的二进制没有执行权限,或 build.bin 路径与 build.cmd 输出路径不一致。
实操解法:
-
build.cmd改成:go build -o ./tmp/main . && chmod +x ./tmp/main -
build.bin必须严格等于./tmp/main(开头带./,不能是tmp/main或./main) - 确保
./tmp目录可写;若挂载在 Docker 中,宿主机对应目录也要有写权限
Docker 内热重载:bind mount + air 是目前最稳组合
不要在宿主机跑 air、再把二进制拷进容器;也不要用 volume 挂载整个 bin/ 目录——那只是伪热重载。真正可行的是:
在 docker-compose.yml 中用 bind mount 把本地源码目录挂进容器(如 ./:/app),然后在容器内安装并运行 air。这样 air 监听的是容器内挂载后的文件系统,构建也在容器环境完成,环境一致性、依赖版本、CGO 行为全部对齐。
关键点:
- 容器内需安装
gcc(如有 CGO)、git(如依赖 git 信息)、以及air本身 - 入口命令设为
air,不是go run main.go;否则无法触发热重载 - 若用 Delve 调试,可在同一容器内启动
dlv --headless --continue --accept-multiclient exec ./tmp/main,再从宿主机远程 attach
复杂点往往不在工具本身,而在路径、权限、挂载层级和构建上下文这四者的咬合精度——少对一环,就卡在“明明改了却不生效”。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











