跳到內容

疑難排解

大多數問題會先反映在 fork 的狀態上,然後才在其他地方出現。先找到 fork、讀取它的狀態,再依照該狀態、你看到的日誌訊息或症狀,查閱對應的那一列。

Graft 記錄外掛失敗時會附上完整的 Cause,並帶有 graft.plugin 和 graft.fork 註解。正在等待的 fork 不會寫入任何日誌:Pending 和長時間的 Loading 只會出現在 fork 樹中。

查看位置 顯示內容
graft start 的輸出 失敗及其 Cause,以及下列訊息。外掛的日誌行會標明 fork。
Graft.forks 每個 fork 的 name、id 和 state,包括 Pending 的 missing 與 Failed 的 cause。
fork.state 你自己載入的單一 fork,直到 Disposed 為止的每次變化。

要記錄一直停在 Pending 的 fork,請加入用 fork 樹診斷中的外掛。 http-gateway 範例透過 HTTP 伺服器掛載 GET /_graft,顯示即時外掛樹。

狀態 要檢查什麼
長時間處於 Loading apply 尚未回傳。在外掛整個生命週期內持續執行的工作,例如 Stream.runForEach、伺服器迴圈或 Effect.never,應放進 Plugin.fork。模組匯入一直沒有完成的設定項目也會停在 Loading。
Pending 請參閱 Pending。
Failed 讀取 cause:在錯誤中找到它的錯誤標籤,修正輸入、相依項目或程式碼。fork 何時再次嘗試,請參閱失敗與重試。
Active,但沒有任何動靜 請參閱 Active 但沒有動靜。

missing 列出必要服務的鍵字串,這些服務在 fork 的領域和環境中都沒有提供者。依序檢查:

  1. 提供者載入了嗎? 處於 Failed、Pending 或已停用的提供者不會發布任何東西。在樹中找到它。
  2. 它在同一個領域嗎? 位於隔離該鍵的 fork 之內的提供者,只會在該領域中發布。請參閱領域。
  3. 鍵相符嗎? 服務依鍵字串(例如 app/Database)完全比對。設定項目在 graft.yml 中的 inject 欄位會讓它等待更多鍵。
  4. 兩個外掛在互相等待嗎? 如果雙方各自注入對方發布的服務,兩者都會停在 Pending。把共用的部分移到第三個外掛,讓兩者都注入它。
原因 修正方式
工作以 Effect.forkChild 啟動。它會在 apply 回傳時被中斷,而中斷不會讓外掛失敗。 改用 Plugin.fork 啟動。
以 Effect.forkScoped、原生計時器或分離的 promise 啟動的工作悄悄失敗了。 Plugin.fork 會回報失敗:外掛失敗,並且可以重試。請參閱外掛中的 Effect。
處理函式或資源是在 Effect.scoped 內或在某次事件處理函式呼叫中設定的。 它隨著那個 scope 一起結束了。請在 apply 中註冊。請參閱處理函式的 context。
啟動時缺少某個可選相依項目。 外掛在沒有它的情況下執行,等它出現時再重新啟動。如果外掛少了它就無法運作,請改為必要相依。
任務捕捉每個錯誤後繼續執行。 讓你無法處理的失敗使任務失敗。請參閱外掛中的 Effect。

與外掛相關的訊息列在 Graft 記錄的內容中。下列來自 graft start 和 loader 的訊息,指出程式碼或設定有問題:

訊息 含義
Restart required… 某個模組變更無法熱重載,舊的外掛繼續執行。請參閱重載不適用的情況。
Reloading plugin modules failed 新程式碼無法匯入或未通過檢查,舊的外掛繼續執行;修正程式碼後再儲存一次。
Kept the previous code: plugin "<name>" … 新程式碼已匯入,但外掛啟動失敗或逾時。舊外掛重新執行。啟動失敗的日誌帶有 cause,逾時沒有 cause;修正程式碼後再儲存一次。
Waiting for valid configuration in <file> 在 --watch 下,第一份 graft.yml 無效。請參閱第一份設定無效。
Dropped an edit to "<id>": the file changed it too 一次 API 編輯輸給了檔案中的編輯。請參閱與外部編輯的衝突。
症狀 原因與修正
對服務 yield* 時出現型別錯誤 該鍵不在 inject 中,或被列為 Plugin.optional:可選的服務請用 Effect.serviceOption 讀取。請參閱相依。
型別錯誤中提到 Tasks 某個輔助函式標註的型別丟掉了 Plugin.Tasks。請參閱型別中的任務錯誤。
defect Plugin.fork requires a plugin activation Plugin.fork 在外掛之外執行,例如在宿主程式中。請從外掛中呼叫它;在宿主中,請用 scope 持有 fiber。
fork.await 或 loader.ready 已回傳,但外掛沒有在執行 兩者都把 Pending 視為已完成,ready 也把 Failed 的 fork 算作已完成。請在 fork.state 上等待 Active。請參閱await、stable 和 state。
另一個外掛載入或重載時,某個消費者跟著重新啟動 它的提供者重新啟動了,而消費者一定會隨之重新啟動;讓提供者原地更新。或者消費者使用了可選相依或環境後備,而它的領域中出現了提供者:請參閱查找順序。
外掛一再重新啟動 每次 retry 嘗試都以相同方式失敗。請讀取記錄下來的 cause;排程無法修正持續存在的錯誤。
消費者看到的是環境中的值,而不是發布的值 提供者在另一個領域中發布。請參閱環境後備與繼承。
處理函式從未被呼叫 派發在另一個領域中執行、被過濾器排除、鍵字串不同,或它的 scope 已經關閉。此外,Event.emit 在第一個失敗處停止,Event.bail 則在第一個 Some 處停止。
處理函式看到錯誤的服務、設定或日誌註解 處理函式使用自己所屬外掛的 context 執行,而不是派發端的 context。請把它需要的東西作為事件引數傳入。請參閱處理函式的 context。
編輯 graft.yml 沒有效果 graft start 執行時沒有加上 --watch;檔案無效,Graft 保留最後一個有效版本(無效的檔案);或者更新函式回傳 true,卻沒有套用變更的欄位。
變更環境變數沒有效果 !env 在設定項目載入、變更或重新整理時讀取,而不是在行程環境變更時。使用 --watch 時,儲存 --env-file 指定的檔案會重新整理它。請參閱何時重新讀取值。
編輯模組沒有效果 graft start 執行時沒有加上 --watch;檔案位於設定檔目錄之外或在 node_modules 中;設定項目載入的是建置後的 JavaScript,而它沒有重新建置;或者重載失敗了。請參閱什麼會觸發重載。
重載之後舊行為仍在繼續 某個模組在匯入時啟動了計時器或連線,Graft 無法停止它。把它移進 apply。請參閱外掛模組的規則。
設定項目以 ConfigError 失敗 某個 !env 變數在行程環境、--env-file 檔案和 --env-dir 目錄中都沒有設定。只有這個設定項目失敗。設定該變數後重新啟動;在 --watch 下也可以儲存 env 檔案(loader.retry 會重新讀取)。請參閱環境變數。
設定項目以 ModuleNotFound 失敗 它的 name 無法解析;訊息會列出嘗試過的名稱。請參閱外掛名稱。
設定項目以 ModuleInvalid 失敗 模組無法匯入,或者既沒有以預設匯出、也沒有以 plugin 匯出外掛。
graft start 立即結束 沒有 --watch 時第一份設定無效,或者找不到 --env-file 指定的檔案或 --env-dir 指定的目錄。請參閱 graft start。
套件管理器列出兩個版本的 effect 或 @graftland/graft 某個外掛套件直接相依於它們。它們只能有一份。請參閱外掛套件。
Remote 外掛忽略程式碼編輯 沒有 --watch 時,請重新啟動宿主。有 --watch 時,如果設定項目指向建置後的 JavaScript,請重新建置;網路 runner 的程式碼不受監看。請參閱設定變更與熱重載。

提供者要等到其服務的消費者都停止之後才會停止,順序見服務撤回流程。這個過程超過 10 秒時,Graft 會記錄 Still waiting for consumers of <keys>; a teardown cycle may be holding them 並繼續等待;關閉、重新啟動和熱重載時都是如此。

要找的情況 修正方式
消費者的 finalizer 在等待某件提供者停止後才會做的事。 不要在 finalizer 中等待提供者;釋放資源後就返回。
經由服務或事件形成的環:A 的清理等待 B,而 B 的清理等待 A。 用第三個外掛擁有共用的部分,打破這個環。
消費者在 Effect.uninterruptible 中的任務等待某件永遠不會來的事。 讓等待保持可中斷,只把關鍵步驟設為不可中斷。

停止卡住卻沒有任何警告時,可能是某個模組匯入一直沒有完成;請參閱限制。

在 Cause 中找到錯誤標籤,再到錯誤中列出該標籤的章節,查看該怎麼處理。

標籤 章節
SchemaError、Fork.CleanupError、任務失敗 fork 失敗
ServiceConflict 服務
Graft.ReplacementConflict 取代
UnregisteredReference、InvalidIntercept Intercept
CyclicConfig、InvalidOperation、StoreError、LoaderClosed Loader 與設定檔
HmrError、Skipped 結果 熱重載
RemoteError、Contract.UndeclaredFailure、RemoteWithdrawn、RemoteEntryError Remote