iT邦幫忙

2026 iThome 鐵人賽

DAY 18
0
Claude AI

從零開始 Claude Code:30 天打造 AI 任務管理 Web App系列 第 18 篇

[Day 18] 程式開始變多:錯誤處理與除錯

  • 分享至 

  • xImage
  •  

做到 Day 17 之後,這個專案已經不只是幾個簡單頁面。
目前已經有:

新增任務
查看任務
編輯任務
刪除任務
快速修改狀態
狀態篩選
分類篩選
MySQL

功能變多之後,下一個一定會遇到的問題就是:

程式出錯時,我到底要先看哪裡?

所以 Day 18 不新增功能,也不重構。
今天只做一件事:
練習看懂錯誤、找到錯誤位置,再判斷要不要修。


今天的範圍

今天主要看這幾種情況:

404 Not Found
400 Bad Request
500 Internal Server Error
Traceback
ValueError
資料庫連線錯誤

今天不做:

分類 CRUD
搜尋
分頁
services/
routes/
Claude API

也不會因為看到錯誤就立刻叫 Claude Code 全部重寫。


先讓 Claude Code 規劃

我先把目前專案狀態交給 Claude Code:

目前 Task CRUD、MySQL 儲存、篩選都已經完成。

今天不加功能、不重構、不建立 services/routes。

目的是練習除錯流程。

請針對幾種常見錯誤情境說明:

1. 怎麼觸發
2. 瀏覽器會看到什麼
3. 終端機會看到什麼
4. 有沒有 Traceback
5. Traceback 要先看哪裡
6. 目前程式是已經處理,還是會直接噴 500

先分析,不要修改任何檔案。

最後決定先測:

1. 找不到 task_id → 404
2. 非法 status → 400
3. 日期格式錯誤 → 500
4. MySQL 執行中斷線 → 500

情境 1:找不到 task_id

目前編輯、刪除、更新狀態都有:

Task.query.get_or_404(task_id)

所以可以直接測:

http://127.0.0.1:5000/tasks/9999/edit

假設:

id = 9999

不存在,Flask 就會回:

404 Not Found

404 實際看到什麼?

實測結果:

HTTP Status:
404

瀏覽器:
Flask 預設 Not Found 頁面

終端機:
只有 access log
沒有完整 Traceback

例如:

GET /tasks/9999/edit HTTP/1.1" 404

一開始我以為只要是錯誤都會有 Traceback。
但這次沒有。
原因是 404 是程式預期中的 HTTP 錯誤回應。

get_or_404()

已經把「找不到資料」轉成 Flask 可以正常回應的 404。
所以可以先這樣理解:

404
→ 要找的資源不存在
→ Flask 已經正常處理
→ 通常不會出現未處理例外的 Traceback

情境 2:非法 status

Day 16 的狀態更新有:

new_status = request.form.get("status")

if new_status not in Task.STATUS_LABELS:
    abort(400)

目前合法值只有:

todo
in_progress
done

正常首頁不會送其他內容。
所以我用 Postman 故意送:

status = bogus

Postman 測法

Method:

POST

網址:

http://127.0.0.1:5000/tasks/2/status

Body 選:

x-www-form-urlencoded

內容:

KEY      VALUE
status   bogus

結果:

400 Bad Request

400 實際看到什麼?

實測結果:

HTTP Status:
400

瀏覽器 / Postman:
Bad Request

終端機:
只有 access log
沒有完整 Traceback

例如:

POST /tasks/2/status HTTP/1.1" 400

這跟 404 很像。
因為:

abort(400)

也是程式刻意回傳的 HTTP 錯誤。
所以:

400 / 404

不一定代表程式壞掉。
有時候反而代表:

後端驗證有正常作用

情境 3:日期格式錯誤

目前新增任務和編輯任務都有:

due_date = (
    datetime.strptime(due_date_text, "%Y-%m-%d").date()
    if due_date_text
    else None
)

正常瀏覽器使用:

<input type="date">

通常會送:

2026-10-02

所以一般操作不容易出錯。
但如果不用瀏覽器,直接用工具送:

due_date = not-a-date

就會打到:

datetime.strptime(...)

實際測試結果

這次真的出現:

500 Internal Server Error

跟前面的 404、400 不一樣。
瀏覽器看到:

Werkzeug 互動式除錯頁面

終端機則出現:

完整 Python Traceback

這才是典型的「程式沒有處理住的例外」。


Traceback 要怎麼看?

這次最後幾行大概是:

File "app.py", line 57, in new_task
    datetime.strptime(due_date_text, "%Y-%m-%d").date()

ValueError: time data 'not-a-date' does not match format '%Y-%m-%d'

我現在會從最下面開始看。
先看:

ValueError

再看:

time data 'not-a-date' does not match format '%Y-%m-%d'

就知道問題是:

日期格式不符合

再往上找第一個:

app.py

就能知道是哪一行自己的程式造成的。


為什麼不要從 Traceback 最上面開始看?

Traceback 上面通常會有很多:

Flask
Werkzeug
SQLAlchemy
Python 內部套件

第一次看很容易被嚇到。
但真正有用的通常是:

最後一行
→ 錯誤類型和原因

往上第一個自己專案的檔案
→ 錯誤發生位置

這次就是:

ValueError
   ↓
app.py
   ↓
datetime.strptime(...)

所以可以很快排除:

不是 CSS
不是 Jinja2
不是 MySQL

而是日期轉換。


這次還意外發現另一個問題

第一次用 curl 測日期格式時,因為 Windows 終端機中文編碼的關係,送出去的資料有問題。
結果意外建立了一筆:

title = ""

的任務。
測試資料後來已經刪掉。
但這件事反而讓我發現一個真實問題。


HTML required 不是後端驗證

新增任務頁目前有:

<input type="text" name="title" required>

所以正常用瀏覽器時,標題空白會被擋住。
但:

required

只是在瀏覽器端驗證。
如果直接用:

Postman
curl
Python

送 POST,就可以繞過。
目前後端是:

title=request.form.get("title", "")

並沒有再檢查:

title 是否為空

所以仍然可能把空標題存進資料庫。


這個問題今天先不修

比較完整的後端驗證未來可以像:

title = request.form.get("title", "").strip()

if not title:
    abort(400)

但 Day 18 的目標是:

學會發現問題
學會看 Traceback

不是開始補表單驗證功能。
所以今天先記下:

額外發現:
後端尚未驗證 title 不可為空

之後再處理。


情境 4:資料庫連線失敗

資料庫錯誤又是另一種類型。
目前 app.py 啟動時有:

try:
    with app.app_context():
        db.session.execute(text("SELECT 1"))
    print("MySQL 連線成功")
except Exception as e:
    print("MySQL 連線失敗:", e)

所以啟動檢查失敗時,終端機會看到:

MySQL 連線失敗:...

但這不代表資料庫錯誤都處理好了

要注意:

app.run(debug=True)

還是在 try/except 後面。
所以即使:

SELECT 1

失敗,Flask 伺服器還是可能繼續啟動。
之後只要進入需要查資料庫的頁面,例如:

/

還是可能再次遇到真正的資料庫例外。


執行中斷線

另一種情況是:

Flask 已經正常啟動
	 ↓
MySQL 突然停止
	 ↓
重新整理首頁

這時 index() 會執行:

Task.query

但資料庫已經連不上。
可能看到:

sqlalchemy.exc.OperationalError

或:

pymysql.err.OperationalError

瀏覽器會是:

500

終端機也會有完整 Traceback。


怎麼辨認資料庫問題?

看到 Traceback 時,一樣從最下面往上看。
如果看到:

OperationalError
pymysql
sqlalchemy

就應該先想到:

資料庫連線

而不是跑去改:

index.html
CSS
Jinja2

可以先檢查:

MySQL80 有沒有啟動
DB_HOST
DB_PORT
DB_NAME
DB_USER
DB_PASSWORD

400、404、500 現在比較看得懂了

今天實際測試後,可以先整理成:

404
→ 找不到資源
→ 預期中的 HTTP Response
→ 通常沒有完整 Traceback

400
→ 請求內容不合法
→ 預期中的 HTTP Response
→ 通常沒有完整 Traceback

500
→ 程式執行時發生未處理例外
→ 瀏覽器看到錯誤頁
→ 終端機看到完整 Traceback

這三種不能混在一起看。


我現在會怎麼讀 Traceback?

以前看到一大串紅字,很容易從第一行開始慢慢讀。
Day 18 之後,我會先:

1. 看最下面
	 ↓
2. 找錯誤類型
	 ↓
3. 看錯誤訊息
	 ↓
4. 往上找第一個自己的檔案
	 ↓
5. 看那一行程式

例如這次:

ValueError
   ↓
not-a-date
   ↓
app.py
   ↓
datetime.strptime(...)

問題範圍一下就縮小很多。


Claude Code 除錯也不要直接叫它修

以前可能會說:

壞掉了,幫我修。

但這樣 Claude 很可能只能自己猜。
現在比較好的方式是:

我做了什麼
我預期什麼
實際發生什麼
錯誤訊息是什麼

一起給它。
例如:

我對 /tasks/new 送出 POST。

due_date 使用 not-a-date。

預期:
後端不要直接噴 500。

實際:
終端機出現:

ValueError: time data 'not-a-date'
does not match format '%Y-%m-%d'

請先分析:

1. 錯誤類型
2. 發生位置
3. 發生原因
4. 最小修改方式

先不要修改程式。

這樣我自己也可以一起理解。


為什麼先分析,不要先修改?

如果真正問題只是:

日期格式

結果 Claude 想一次改:

app.py
models/task.py
schema.sql
templates/

那就很可疑。
一個小 bug 突然牽涉很多檔案,有可能是在:

順便重構

所以我現在會先問:

這個問題最小修改範圍是什麼?

確認後才讓它改。


修完還是要自己重新測

就算 Claude Code 最後說:

已修復

也不代表真的完成。
還是要重新走一次:

原本會出錯的操作

例如:

POST 錯誤日期

如果真的有修,就重新測。
還要確認:

原本正常功能沒有被改壞

所以完整流程應該是:

重現
 ↓
分析
 ↓
修改
 ↓
重新重現
 ↓
確認修好

Day 18 的除錯流程

今天最後把自己的流程整理成:

1. 先重現問題
		↓
2. 記錄剛剛做了什麼
		↓
3. 看 HTTP Status
		↓
4. 看瀏覽器錯誤頁
		↓
5. 看終端機 Traceback
		↓
6. 從最後一行找錯誤類型
		↓
7. 往上找第一個自己的檔案
		↓
8. 判斷問題在哪一層
		↓
9. 再交給 Claude Code 分析
		↓
10. 用最小範圍修正
		↓
11. 重新做原本操作驗證

這比:

   出錯
    ↓
直接叫 AI 修

更可靠。


今天實際完成的測試

這次已經實際確認:

找不到 task_id
→ 404
→ 無完整 Traceback

非法 status
→ 400
→ 無完整 Traceback

錯誤日期格式
→ 500
→ ValueError
→ 有完整 Traceback

也理解了資料庫連線錯誤時,要從:

OperationalError
PyMySQL
SQLAlchemy

這類關鍵字去判斷問題。


Day 18 做完後

今天沒有增加任何新功能。
但我現在開始比較能分辨:

這是預期中的 HTTP 錯誤

還是:

這是真的程式例外

也開始知道看到一大串 Traceback 時,不需要全部從頭看。
先找:

錯誤類型
錯誤訊息
自己的檔案
出錯行數

就能把問題範圍縮小。
而 Claude Code 在除錯時最有用的地方,也不是直接替我亂改,而是幫我:

讀錯誤訊息
縮小範圍
解釋原因
提出最小修正

Day 18 到這裡完成。
下一步會繼續練習另一件更重要的事:
Claude Code 說「修好了」,我要怎麼知道它真的修好了?


上一篇
[Day 17] 任務開始變多:加入分類與篩選
下一篇
[Day 19] 讓 Claude Code 幫我修 Bug,但不能只相信它說「修好了」
系列文
從零開始 Claude Code:30 天打造 AI 任務管理 Web App 共 20 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言