摘要
POST /api/lark/bind 用真實、有效的 Lark App 憑證綁定時,後端實際上完全綁定成功(DB 記錄、Lark 憑證驗證、bot 身份都正確建立),但 HTTP 回應卻是 500 Internal Server Error(純文字,不是這個專案慣用的 {"detail": ...} 或 {"success": false, "error": ...} JSON 格式)。前端因此顯示綁定失敗,使用者會誤以為需要重試或憑證有誤,但實際上第一次就已經成功了。
100% 可重現,且與 owner_email 欄位是否帶值無關(兩種情況都一樣壞掉)。
重現步驟
環境:https://dev-agent.narra.nexus,測試帳號的一次性 agent,使用真實 Lark App(app_id cli_a9ce...,App Secret 已妥善保密不列出)
- 頻道面板 → Lark / Feishu → 填入真實 App ID + App Secret + 選擇「Lark(國際版)」
- 呼叫
POST /api/lark/bind → HTTP 500,body 為純文字 Internal Server Error(非 JSON)
- 前端顯示「未綁定」+「API error: 500」的錯誤提示
- 手動點擊面板的「刷新」按鈕 → 顯示「✓ 已連接」,機器人已連接,App ID 正確顯示,綁定其實是成功的
- 解綁後,不帶
owner_email 重新綁定同一組憑證 → 一樣是 500,刷新後一樣顯示已連接成功
已排除的根因(code review)
追過三個 do_bind() 流程裡最複雜、最可能拋出未捕捉例外的步驟,三者的程式碼本身都有妥善的例外處理,不像是問題所在:
resolve_owner()(owner_email 查詢)——已用不帶 owner_email 的重現排除,兩種情況結果一致
probe_event_subscription()(_lark_event_probe.py,spawn lark-cli event +subscribe 監聽約 5 秒驗證事件訂閱)——subprocess 建立、逾時等待、清理都包在 try/except/finally 裡
check_app_scopes()(_lark_scope_validator.py)——docstring 明確聲明「never raises」,內部對各種 CLI 回傳格式都有防禦性解析
因此懷疑落在:do_bind() 裡緊接著 scope check 之後的 bot-info 取得步驟(_cli._run_with_agent_id(["api", "GET", "/open-apis/bot/v3/info", "--as", "bot"], agent_id) 及後續的 mgr.update_bot_identity(...)),或是 route handler 層(backend/routes/channels/lark.py 的 bind_lark_bot)回傳 bind_result 之後、FastAPI 序列化回應之前的某個環節。沒有 server log 存取權限,無法精確定位到行號,需要 dev 對照當下時間點(測試時間見下)的 log 找出實際 traceback。
影響
- 使用者體驗:真實使用者用有效憑證綁定 Lark 時,會看到「失敗」,但實際上已經成功——可能導致使用者誤判憑證有問題去重新申請、或反覆重試(重試會撞到
do_bind() 裡「Agent already has a Lark bot bound」的檢查,變成看起來像另一種錯誤,更加混亂)
- 500 回應是純文字而非這個專案慣用的結構化 JSON 錯誤格式,代表這是一個真正未被任何 handler 接住的例外,不是刻意設計的錯誤路徑
- 這跟同一個檔案(
lark.py)先前私下記錄過的「內部路徑洩漏」觀察是不同問題,這次是「假失敗」而非資訊洩漏
建議
- 用測試時間點(
2026-08-14 約 UTC 03:17 及 03:28 兩次,agent_id 見下)對照 server log 找出實際拋出的例外與行號
do_bind() 裡從 bot-info 取得開始到函式回傳之間的步驟,建議補上外層 try/except,讓「核心綁定已成功但後續 best-effort 步驟失敗」時仍然回傳 {"success": true, ...} 加上 warning,而不是讓整個 request 變成 500(跟前面 scope check / event probe 已經做到的「fail-open on tooling errors」原則保持一致)
測試環境
https://dev-agent.narra.nexus
- 測試 agent:一次性測試 agent(非正式使用中的 agent)
- 程式碼:
src/xyz_agent_context/module/lark_module/_lark_service.py(do_bind)、backend/routes/channels/lark.py(bind_lark_bot)
- 對應測試計畫項目:
qa/features/channels/test-plan-channels.md TC-CHAN-LARK-02
摘要
POST /api/lark/bind用真實、有效的 Lark App 憑證綁定時,後端實際上完全綁定成功(DB 記錄、Lark 憑證驗證、bot 身份都正確建立),但 HTTP 回應卻是500 Internal Server Error(純文字,不是這個專案慣用的{"detail": ...}或{"success": false, "error": ...}JSON 格式)。前端因此顯示綁定失敗,使用者會誤以為需要重試或憑證有誤,但實際上第一次就已經成功了。100% 可重現,且與
owner_email欄位是否帶值無關(兩種情況都一樣壞掉)。重現步驟
環境:
https://dev-agent.narra.nexus,測試帳號的一次性 agent,使用真實 Lark App(app_idcli_a9ce...,App Secret 已妥善保密不列出)POST /api/lark/bind→ HTTP 500,body 為純文字Internal Server Error(非 JSON)owner_email重新綁定同一組憑證 → 一樣是 500,刷新後一樣顯示已連接成功已排除的根因(code review)
追過三個
do_bind()流程裡最複雜、最可能拋出未捕捉例外的步驟,三者的程式碼本身都有妥善的例外處理,不像是問題所在:resolve_owner()(owner_email 查詢)——已用不帶 owner_email 的重現排除,兩種情況結果一致probe_event_subscription()(_lark_event_probe.py,spawnlark-cli event +subscribe監聽約 5 秒驗證事件訂閱)——subprocess 建立、逾時等待、清理都包在 try/except/finally 裡check_app_scopes()(_lark_scope_validator.py)——docstring 明確聲明「never raises」,內部對各種 CLI 回傳格式都有防禦性解析因此懷疑落在:
do_bind()裡緊接著 scope check 之後的 bot-info 取得步驟(_cli._run_with_agent_id(["api", "GET", "/open-apis/bot/v3/info", "--as", "bot"], agent_id)及後續的mgr.update_bot_identity(...)),或是 route handler 層(backend/routes/channels/lark.py的bind_lark_bot)回傳bind_result之後、FastAPI 序列化回應之前的某個環節。沒有 server log 存取權限,無法精確定位到行號,需要 dev 對照當下時間點(測試時間見下)的 log 找出實際 traceback。影響
do_bind()裡「Agent already has a Lark bot bound」的檢查,變成看起來像另一種錯誤,更加混亂)lark.py)先前私下記錄過的「內部路徑洩漏」觀察是不同問題,這次是「假失敗」而非資訊洩漏建議
2026-08-14約 UTC 03:17 及 03:28 兩次,agent_id 見下)對照 server log 找出實際拋出的例外與行號do_bind()裡從 bot-info 取得開始到函式回傳之間的步驟,建議補上外層 try/except,讓「核心綁定已成功但後續 best-effort 步驟失敗」時仍然回傳{"success": true, ...}加上 warning,而不是讓整個 request 變成 500(跟前面 scope check / event probe 已經做到的「fail-open on tooling errors」原則保持一致)測試環境
https://dev-agent.narra.nexussrc/xyz_agent_context/module/lark_module/_lark_service.py(do_bind)、backend/routes/channels/lark.py(bind_lark_bot)qa/features/channels/test-plan-channels.mdTC-CHAN-LARK-02