新手看錯誤訊息的方式幾乎都是同一種:從第一行開始,逐字讀,讀到第二十行發現自己一個字都沒看懂,然後關掉視窗、重裝。
問題不在你讀不懂英文,也不在你不懂程式。問題在沒有人告訴過你這則訊息有形狀。
它有。而且形狀很固定:
| 位置 | 是什麼 | 對你的價值 |
|---|---|---|
| 最後一行(或最後幾行) | 結論 | 最高。九成的時候你要的答案就在這裡 |
| 中間一大段 | 堆疊追蹤(stack trace) | 低,但有一個例外,見下 |
| 散在中間與結尾 | 檔名與行號 | 高。有行號就直接去看那一行 |
| 最後,常常被忽略 | 結束代碼(exit code) | 高。它告訴你「到底成功了沒有」,比輸出文字誠實 |
這篇教你怎麼按這個順序拆一則你完全看不懂的訊息。
四步驟的短版、以及七種常見結尾行對照表,在 Terminal、Shell、CLI:三個詞的差別 裡已經有一份。這篇不重複那張表 —— 這裡講的是那張表背後的原則:為什麼是倒過來讀、堆疊追蹤到底在說什麼、以及拿到一則訊息之後你該把它給誰。
步驟
步驟一:捲到最後,只讀最後一行
不管這則訊息有多長,先做這一件事:捲到最底部。
多數工具把結論放在最後。原因是它一路做下來,做到某一步失敗了,然後把失敗的原因印出來收尾。中間那一大段是過程紀錄,結尾才是它的判斷。
最後一行通常很短。抄下來。它幾乎一定會落在下面幾種形狀之一:
- 一句英文的結論,例如
No such file or directory、Permission denied、command not found - 一個類別名稱加一句話,例如
ModuleNotFoundError: No module named 'requests' - 一個三位數的數字,例如
401、403、429 - 一個全大寫的底線分隔識別碼,例如
UnauthorizedAccess、EACCES、ENOTFOUND
這一行的資訊量,通常比前面三十行加起來還高。
步驟二:往上找第一個 Error、error: 或 failed
從最後一行往上捲,找第一個出現這些字樣的地方。
為什麼是「第一個」而不是「最後一個」:很多工具在真正失敗之後,還會印一段收尾訊息、一段「請參考文件」、或一段上層呼叫端的抱怨。上層的抱怨只是轉述,第一個出錯點才是原因。
舉例(這是範例,不是任何工具的真實輸出):
[1/4] Reading config... ok
[2/4] Resolving dependencies... ok
[3/4] Fetching package foo==1.2.0
error: failed to fetch https://registry.example.invalid/foo-1.2.0.tar.gz
caused by: connection refused
[4/4] aborted
See https://example.invalid/docs/troubleshooting for help.
倒著讀的人會先看到第 4 行的 aborted(沒資訊量)與最後那行文件連結(也沒資訊量)。真正的原因在 error: 那一行,而它的下一行 caused by: 給了你更精確的版本 —— caused by 這個詞後面接的,幾乎永遠比它前面那句有用。
步驟三:找檔名與行號
在整段訊息裡掃這兩個形狀:
File "app.py", line 42, in main
src/main.ts:17:5
/path/to/script.sh: line 8: foo: command not found
三種分別是 Python、TypeScript/很多編譯器、以及 shell 腳本的寫法,但結構一樣:哪個檔案、第幾行。
src/main.ts:17:5是「第 17 行、第 5 個字元」script.sh: line 8是「第 8 行」
有行號就直接去開那個檔案、看那一行。 這比讀懂整則訊息快得多,而且你不需要看懂訊息也能做。
如果那個檔案不是你的(路徑在某個套件庫或系統目錄底下),那行號對你沒有用 —— 這代表錯誤發生在你呼叫的別人的程式碼裡面,你要看的是你自己那一行是怎麼呼叫它的,也就是堆疊追蹤裡下一個屬於你的檔案。這就接到下一段。
步驟四:讀堆疊追蹤,由下往上
堆疊追蹤(stack trace)是新手最容易被淹沒的一段。它長這樣(範例):
Traceback (most recent call last):
File "/Users/you/project/main.py", line 12, in <module>
result = load_config(path)
File "/Users/you/project/config.py", line 34, in load_config
return json.loads(text)
File "/Library/Frameworks/Python.framework/Versions/3.13/lib/python3.13/json/__init__.py", line 346, in loads
return _default_decoder.decode(s)
File "/Library/Frameworks/Python.framework/Versions/3.13/lib/python3.13/json/decoder.py", line 338, in decode
obj, end = self.raw_decode(s, idx=_w(s, 0).end())
json.decoder.JSONDecodeError: Expecting ',' delimiter: line 7 column 3 (char 112)
為什麼要由下往上讀。
堆疊追蹤列的是「呼叫鏈」:最上面是你程式的入口,往下每一層是被上一層呼叫的函式,最下面那一層是執行到一半真正出事的地方。所以:
- 最後一行(最底層)是結論:
JSONDecodeError: Expecting ',' delimiter: line 7 column 3。它告訴你:某個 JSON 檔案第 7 行第 3 個字元少了逗號。 - 從下往上找第一個「你自己的檔案」:上面那個範例裡是
config.py第 34 行。這是你需要打開的檔案。 - 中間那些框架與標準函式庫的路徑,全部跳過。 那不是你的程式碼,你也改不了它。看不懂不是你的問題。
一句話版本:最底層告訴你「錯在哪」,往上第一個屬於你的檔案告訴你「去改哪」。
這個讀法對 Python、JavaScript、Java、Go、Rust 都成立,雖然措辭不同(JavaScript 寫 at functionName (file:line:col),而且順序相反 —— 最上面才是出事的地方)。所以先認一件事:你手上這則是哪種語言的。認不出來就看檔名副檔名(.py、.js、.ts、.go)。
步驟五:看結束代碼,確認它到底成功了沒有
這是多數新手從來沒看過的一個數字,而它是整則訊息裡最誠實的部分。
結束代碼(exit code)是程式結束時交回的一個整數:0 代表成功,非 0 代表失敗。見 Exit Code(結束代碼)。
在 macOS/Linux 的 shell 裡,跑完一條指令之後立刻:
echo $?
Windows PowerShell:
$LASTEXITCODE
為什麼這個數字比輸出文字可靠:一個程式可以印出一大堆看起來完全正常的進度訊息,然後以非 0 結束;也可以刷一屏警告,結束代碼卻是 0。腳本、CI 系統、以及 AI Agent 判斷「上一步成不成功」,靠的都是這個數字,不是閱讀輸出文字。
這帶來一個非常實際的後果:如果你把一則錯誤訊息貼給 AI Agent 或貼到論壇,附上結束代碼,對方就知道該不該繼續往下追。 沒有這個數字,對方第一件事通常就是問你「它到底跑成功了沒有」。
順帶一個反直覺的點:echo $? 給的是上一條指令的結束代碼。所以你要在失敗的那條指令之後立刻跑它,中間不能插任何別的指令 —— 插了就被蓋掉。
步驟六:決定這則訊息要給誰
讀完上面五步,你手上有:最後一行、出錯的檔名與行號、結束代碼。現在做一個決定。
情況一:最後一行是一句你看得懂的英文結論。 把它整段(連同你跑的指令)拿去搜尋。加引號搜最後一行原文,比搜你自己的描述準得多 —— 因為別人的錯誤訊息跟你的一模一樣,而別人的描述跟你的不一樣。搜的時候補上工具名稱與版本號。
情況二:你看不懂最後一行,但你有檔名與行號。 直接去開那個檔案那一行。很多時候看一眼就知道(少一個逗號、拼錯一個名字、路徑寫錯)。這比搜尋快。
情況三:訊息很長、有多層 caused by、或涉及好幾個工具。
這是該貼給 AI Agent 的情況。原因很具體:Agent 能在你的機器上跑指令,它可以自己執行步驟一到五,不需要你轉述。你貼給搜尋引擎的是一段死文字,貼給 Agent 的是一個它可以動手調查的現場。
給 Agent 的時候,貼這些(缺一不可):
1. 我跑的完整指令(原樣貼上,不要改寫)
2. 完整的錯誤訊息(原樣貼上,不要節錄、不要翻譯)
3. 結束代碼
4. 作業系統與版本、以及工具的版本號
5. 我已經確認過什麼(例如:which 有印出路徑、磁碟還有空間、換過網路)
第 5 項最常被跳過,而它最省時間 —— 沒有它,Agent 會從頭問一遍你已經做過的事。
情況四:訊息裡有密鑰、密碼、或公司內部網址。 先塗掉再貼給任何人。 錯誤訊息常常會把環境變數的內容印出來,因為它出現在指令參數或設定檔裡。這件事在 API Key 是什麼 裡有完整的說明 —— 貼出去的密鑰要當作已經洩漏處理,光刪掉那則發文沒有用。
搜尋引擎、公開論壇、以及多數 AI 工具的對話內容,都不該出現你的密鑰。
六個真實形狀的範例
以下都是教學用的示意範例,形狀取自常見工具的真實輸出結構,但內容是編的。認形狀,不要背字。
一、指令不存在(PATH 問題)
bash: somecli: command not found
一行,沒有堆疊追蹤。這是 shell 自己在講話,不是那個程式在講話 —— 因為那個程式根本沒被找到。第一個動作是 which somecli,不是重裝。見 PATH 與路徑。
二、檔案不存在,但指令存在
cat: notes.txt: No such file or directory
九成不是檔案消失了,是你站的資料夾不對。先 pwd。見 Working Directory(工作目錄)。
三、權限
chmod: changing permissions of 'run.sh': Operation not permitted
或更常見的:
bash: ./run.sh: Permission denied
兩個看起來像,其實不同。第二個通常只是檔案缺可執行權限,chmod +x run.sh 就解決,跟 sudo(管理員權限) 一點關係都沒有。先 ls -l run.sh 看權限位。見 File Permission(檔案權限)。
四、Node.js 找不到模組(堆疊追蹤由上往下讀的那一種)
node:internal/modules/cjs/loader:1404
throw err;
^
Error: Cannot find module 'express'
Require stack:
- /Users/you/project/index.js
at Module._resolveFilename (node:internal/modules/cjs/loader:1401:15)
at index.js:3:17
注意這裡的結構跟 Python 相反:Error: Cannot find module 'express' 在上面,然後 at ... 一路往下是呼叫鏈。而 Require stack 那段直接告訴你是 /Users/you/project/index.js 第 3 行在要這個模組。
五、HTTP 狀態碼
{"error":{"message":"Incorrect API key provided","type":"invalid_request_error","code":"invalid_api_key"}}
配合結束代碼非 0。這類 JSON 格式的錯誤裡,code 與 type 欄位比 message 好用 —— message 是給人讀的句子,code 是給程式比對的固定字串,拿去搜尋命中率高得多。
401/403/429 這些數字各自的意思見 Status Code(狀態碼) 與 Rate Limit(速率限制)。
六、Windows PowerShell 的執行原則
.\install.ps1 : File .\install.ps1 cannot be loaded. The file .\install.ps1 is not
digitally signed. The script will not execute on the system.
+ CategoryInfo : SecurityError: (:) [], PSSecurityException
+ FullyQualifiedErrorId : UnauthorizedAccess
這是本站從 Microsoft 官方文件裡抄下來的真實輸出(不是示意)。注意最後那個 FullyQualifiedErrorId : UnauthorizedAccess —— 這是 PowerShell 的穩定識別碼,比上面那段句子更適合拿去搜尋。處理方式見 Windows 上安裝失敗 的步驟四。
三個讀錯誤訊息的壞習慣
一、只截圖,不複製文字。 截圖不能被搜尋、不能被 Agent 讀、也不能被別人複製那行關鍵字。終端機的文字是可以選取複製的(macOS 直接在視窗裡拖曳選取,Windows Terminal 也是)。永遠複製文字。
二、只看第一段就下結論。 第一段通常是進度訊息,不是錯誤。真正的結論在最後。
三、同一條指令重跑五次。 同樣的輸入會得到同樣的輸出。第二次之後你拿到的不是新資訊,是同樣的資訊加上你越來越焦躁的情緒。重跑之前先改一個變數 —— 換個資料夾、開個新視窗、換個網路 —— 否則你只是在浪費時間。
下一步
- 讀完之後要動手修:裝完沒反應:五個檢查,照順序做完再重裝
- 平台細節:Windows 上安裝失敗、macOS 上安裝失敗
- 常見結尾行對照表與十行保命指令:Terminal、Shell、CLI:三個詞的差別
- 那個視窗到底是什麼:Terminal 是什麼
- 結束代碼為什麼重要:Exit Code(結束代碼)、Process(處理程序)
- 一個真實的、從頭讀到尾的崩潰分析:一次 PEP 668 崩潰的完整分析