Skip to content

Lark bind 用真實憑證實際成功,但 API 回應卻是 500,前端誤報失敗 #118

Description

@MarkTsaiCqi

摘要

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 已妥善保密不列出)

  1. 頻道面板 → Lark / Feishu → 填入真實 App ID + App Secret + 選擇「Lark(國際版)」
  2. 呼叫 POST /api/lark/bindHTTP 500,body 為純文字 Internal Server Error(非 JSON)
  3. 前端顯示「未綁定」+「API error: 500」的錯誤提示
  4. 手動點擊面板的「刷新」按鈕 → 顯示「✓ 已連接」,機器人已連接,App ID 正確顯示,綁定其實是成功的
  5. 解綁後,不帶 owner_email 重新綁定同一組憑證 → 一樣是 500,刷新後一樣顯示已連接成功

已排除的根因(code review)

追過三個 do_bind() 流程裡最複雜、最可能拋出未捕捉例外的步驟,三者的程式碼本身都有妥善的例外處理,不像是問題所在:

  1. resolve_owner()(owner_email 查詢)——已用不帶 owner_email 的重現排除,兩種情況結果一致
  2. probe_event_subscription()_lark_event_probe.py,spawn lark-cli event +subscribe 監聽約 5 秒驗證事件訂閱)——subprocess 建立、逾時等待、清理都包在 try/except/finally 裡
  3. 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.pybind_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.pydo_bind)、backend/routes/channels/lark.pybind_lark_bot
  • 對應測試計畫項目:qa/features/channels/test-plan-channels.md TC-CHAN-LARK-02

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions