重新連接不是修復,而是換一個帳號

重新連接不是修復,而是換一個帳號

超商的悠遊卡綁定服務,你換了一張新卡,但自動扣款綁的還是舊卡。新卡刷進去沒問題,每個月帳單卻還是從舊卡扣——直到哪天舊卡餘額歸零,才發現這兩件事從來不是同一回事。

一個跑了三個月的整合服務,突然開始回報「物件不存在」。錯誤碼很明確:INTEGRATION_NOT_FOUND,沒有任何人改動過相關設定,沒有部署,沒有更新,什麼都沒有。工作流仍然在跑,只是每次呼叫都失敗。追進去的時候,發現三天前有人從 UI 重新授權了第三方帳號。

重新連接的語義陷阱

那個按鈕上寫的是「重新連接」。聽起來像是恢復中斷的連線,實際上平台建立了一組全新的 integration 記錄,並指派了一個新的 ID。舊的那組 ID 沒有被刪除,也沒有被標記為失效,只是永遠不再有效。平台不會主動通知任何人。中游的工作流仍然帶著舊 ID 在呼叫,從那天起就已經在喊死。

問題不在於 ID 變了,而在於「重新連接」這個操作的語義被誤導了。它不是 restore,是 create。所有引用舊 ID 的地方必須手動更新。沒有 migration,沒有 fallback,沒有 warning。你只會在某個工作流失敗時,才發現這件事。

OAuth 整合的靜默更換

這類靜默更換在 OAuth 整合生態裡不罕見。Slack、GitHub、Google Workspace,幾乎每個平台都有類似的行為。當你重新授權一個 app,平台會視為一次全新的安裝,生成新的 token、新的 webhook URL、新的 integration ID。舊的那組資源不會被回收,也不會被標記為過期,只是靜靜地躺在那裡,等著你的系統繼續呼叫,繼續失敗。

更糟的是,UI 上通常只會顯示「已連接」,不會顯示「這是第幾次連接」或「這是哪一組 ID」。你看到的是狀態,不是歷史。當你的系統還在用舊 ID 呼叫時,UI 告訴你一切正常。

手動更新的成本

發現問題之後,必須手動找出所有引用舊 ID 的地方。環境變數、資料庫記錄、configuration file、CI/CD pipeline。每個地方都要改,每個地方都要測試。如果你的系統有多個環境,那就要改多次。如果你的 ID 被寫死在程式碼裡,那就要重新部署。

這不是技術債,是語義債。平台設計者認為「重新連接」是一次性操作,使用者認為「重新連接」是恢復狀態。這個誤差沒有被文件化,也沒有被 UI 提示。你只能在失敗之後,才知道這件事。

沒有主動通知的設計

三天前那次重新授權,平台沒有發任何通知。沒有 email,沒有 webhook,沒有 audit log 裡的 warning。舊 ID 仍然存在於資料庫裡,只是不再有效。當你用舊 ID 呼叫 API 時,回傳的是 404,不是 410。404 告訴你「找不到」,410 才會告訴你「曾經存在但已失效」。

這個設計選擇不是疏忽,是刻意的。平台不想承擔「通知所有相關系統」的責任,因為它不知道誰在用這個 ID。OAuth 的設計假設 token 和 ID 都是短期有效的,會定期更新,會有 refresh 機制。但實務上,很多整合系統會把 ID 當作永久識別符,寫進資料庫,寫進配置,寫進文件。當 ID 被靜默更換時,所有這些假設都會失效。

— 邱柏宇

延伸閱讀


Reconnect Doesn’t Restore — It Replaces

An integration service that had been running smoothly for three months suddenly started reporting “object not found.” The error code was clear: INTEGRATION_NOT_FOUND. No one had touched the configuration. No deployments, no updates, nothing. The workflow was still running, just failing every time it tried to call the API. Digging into it revealed that three days ago, someone had re-authorized the third-party account from the UI.

The Semantic Trap of Reconnection

The button said “Reconnect.” It sounded like restoring a dropped connection. What it actually did was create a brand-new integration record and assign it a new ID. The old ID wasn’t deleted or marked invalid—it just stopped working. The platform didn’t notify anyone. The downstream workflow was still carrying the old ID, and had been silently failing for three days.

The problem isn’t that the ID changed. It’s that “reconnect” misleads you about what the operation does. It’s not restore, it’s create. Every place that references the old ID has to be manually updated. No migration, no fallback, no warning. You only find out when something breaks.

Silent Replacement in OAuth Ecosystems

This kind of silent replacement is common in OAuth integrations. Slack, GitHub, Google Workspace—almost every platform behaves this way. When you re-authorize an app, the platform treats it as a fresh installation: new token, new webhook URL, new integration ID. The old resources aren’t recycled or flagged as expired. They just sit there, waiting for your system to keep calling them and keep failing.

Worse, the UI usually only shows “Connected,” not “this is the third connection” or “this is ID xyz.” You see status, not history. While your system is still calling the old ID, the UI tells you everything is fine.

The Cost of Manual Updates

Once you discover the issue, you have to hunt down every reference to the old ID. Environment variables, database records, configuration files, CI/CD pipelines. Each one has to be changed and tested. If you have multiple environments, you change it multiple times. If the ID is hardcoded, you redeploy.

This isn’t technical debt, it’s semantic debt. Platform designers think “reconnect” is a one-time operation. Users think “reconnect” restores state. This gap isn’t documented or surfaced in the UI. You only learn it after things break.

No Active Notification by Design

Three days ago, when that re-authorization happened, the platform sent no notification. No email, no webhook, no warning in the audit log. The old ID still exists in the database, just no longer valid. When you call the API with the old ID, you get a 404, not a 410. 404 says “not found.” 410 says “used to exist but is gone.”

This design choice isn’t an oversight—it’s intentional. The platform doesn’t want the responsibility of notifying all dependent systems, because it doesn’t know who’s using the ID. OAuth assumes tokens and IDs are short-lived, regularly refreshed. But in practice, many integration systems treat IDs as permanent identifiers, storing them in databases, configurations, documentation. When an ID is silently replaced, all those assumptions break.

— 邱柏宇

Related Posts