Agentic Research
首頁/學習/怎麼讀一則錯誤訊息:解剖、堆疊追蹤與結束代碼

怎麼讀一則錯誤訊息:解剖、堆疊追蹤與結束代碼

2026/09/3019 分鐘Bryan Chan最後更新 2026/09/30

新手看錯誤訊息的方式幾乎都是同一種:從第一行開始,逐字讀,讀到第二十行發現自己一個字都沒看懂,然後關掉視窗、重裝。

問題不在你讀不懂英文,也不在你不懂程式。問題在沒有人告訴過你這則訊息有形狀。

它有。而且形狀很固定:

位置是什麼對你的價值
最後一行(或最後幾行)結論最高。九成的時候你要的答案就在這裡
中間一大段堆疊追蹤(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 也是)。永遠複製文字。

二、只看第一段就下結論。 第一段通常是進度訊息,不是錯誤。真正的結論在最後。

三、同一條指令重跑五次。 同樣的輸入會得到同樣的輸出。第二次之後你拿到的不是新資訊,是同樣的資訊加上你越來越焦躁的情緒。重跑之前先改一個變數 —— 換個資料夾、開個新視窗、換個網路 —— 否則你只是在浪費時間。

下一步