錯誤碼對照表
通則
- 404 = 未授權或不存在。sandbox 刻意「不洩漏存在性」:沒權限、別家公司的 id、受限 key 動了禁區,全都回 404 而非 403。
- 403 是真實例外,不是預設。 建立呼叫者不能擁有的 department/company 任務 →
owner_scope_forbidden。Writeback 發布/提交閘與預算 hold 也是 403。送owner_scope=chatroom是 422,不是 403。 - 422 = 請求驗證。未知欄位(
extra="forbid")、pattern 不符、大小超限、缺必帶 header。預算/hold 定價拒絕也可能是 422。 - 409 = 狀態衝突。非法狀態轉換、樂觀鎖 CAS 不符、digest 不符、不可重試、版本容量耗盡。
- 413 = body 過大。提交執行時依
Content-Length先擋。 - 503 = kill switch。
SANDBOX_ENABLED=false時的變更(預設 true)。
對照表
| 狀態碼 | 錯誤 / 情境 | 何時發生 |
|---|---|---|
| 403 | 受限 Sandbox 金鑰/缺聊天室路徑/invalid scope | 認證層字串,不是 {code}。只發生在 /private/module/sandbox/environments*、/tasks*(含每任務歷史 /tasks/{id}/runs)、/root*、/chatrooms/{id}/quick-run、路徑裡完全沒有 chatroom 片段的路由(/settings、/bindings*、/secrets*、/secret-approvals*),以及路徑上的 chatroom id 不在金鑰的 allowed chatrooms 時。 |
| 405 | 受限 Sandbox 金鑰動了 scope allowlist 以外的其他 sandbox 路由 | granted-jobs(含 enable/disable)、runs/{id}/retry、runs/{id}/{log|output}/public-link 由 scope 檢查直接回 405 Method Not Allowed——不是 403 也不是 404。 |
| 403 | Insufficient permissions. | settings 與每任務歷史的 COMPANY_MANAGER 閘(非 manager JWT)。 |
| 404 | 未授權 / 跨公司 / 資源不存在 | sandbox router 內的藏存在性預設 |
| 404 | 產出物 deletion_state != active 的 download | 下載已刪/墓碑產出物 |
| 403 | owner_scope_forbidden | 建立呼叫者不能擁有的 department/company 任務。這是用 404 藏存在性的例外。 |
| 403 | chatroom_owner_scope_removed | 若 chatroom 目錄建立繞過 enum 進到 CRUD(HTTP POST /tasks 先是 422)。 |
| 403 | output_policy_author_denied | 表格 writeback:發布者對目標表沒有不受限寫入權。Command 輸出(v5.21.0):撰寫者、發布者或提交者沒通過身分、grant 或範圍檢查;該 Command 不再是帶 effect_identity 的 restricted definition-authority Command(發布時是下面的 409);或提交 run 的方式不符合任務輸出交給自訂表格 Command的規則(什麼都不會排隊)。run 進行中該授權被收回時,command-output 端點也回這個。 |
| 403 | writeback_authority_denied | 提交者不能鑄 writeback 憑證。 |
| 403 | budget_reservation_exceeded | 提交執行/佇列建置時,定價 envelope 放不進公司配額;不建 row |
| 422 | idempotency_key_required | 提交/重試/quick-run/secret 寫入缺 Idempotency-Key |
| 422 | 欄位驗證 | 未知欄位、owner_scope=chatroom(enum)、digest pattern、缺少 owned_object_id+source_digest、canonical JSON 超限、空 PUT body |
| 422 | output_policy_invalid/output_policy_table_not_found/output_policy_channel_table | 草稿/發布時 writeback policy 不合法 |
| 422 | output_policy_governed_table | 草稿建立/更新:表格 writeback policy 指向 write_policy 為 commands_only 的表(v5.21.0)。發布與提交 run 時,同樣的狀況是 409 output_policy_unsatisfiable |
| 422 | output_policy_command_not_found/output_policy_schema_mismatch | Command 輸出 policy(v5.21.0),草稿建立/更新時,以及提交 run 時再次檢查:你公司裡沒有這個 id 的活 Command,或 input_schema 與 Command 的 inputs 不同/該 Command 不是寫入型 |
| 422 | 預算/hold 定價拒絕 | 提交時定價或 hold 驗證失敗,例如 no_active_region、missing_price、invalid_envelope、missing_window |
| 422 | timeout_exceeds_ceiling | timeout_seconds > 公司 timeout_ceiling_seconds |
| 422 | job_contract_invalid | 在草稿建立/更新時(POST /tasks/{id}/versions 或 PATCH .../versions/{vid})——發布時絕不會出現:input_example 不是合法 JSON、不符合版本自己的 input_schema;work_command 含 NUL/超過 4096 UTF-8 位元組;或 input_schema 本身不合法——不是 JSON、不是 JSON object、超過 64 KiB(UTF-8 位元組計,非字元數)、含 NUL、或不是合法的 draft 2020-12 schema。回應的 detail.errors 會一次列出所有問題(最多 20 筆) |
| 422 | input_schema_violation | 提交執行時:input 不符合已發布版本的 input_schema。在派工前就拒絕。只有存好的 schema 本身解析不出來或拿不到時才會 fail-open(放行、記警告) |
| 422 | input_not_stageable | POST /chatrooms/{cid}/runs:input JSON 無法 canonicalise 或寫入耐久儲存。發生在房間/版本授權之前,所以不會建出 run。run input 不再計入公司每分鐘 5 次上傳起始額度(後端 ≥ #1140),連續提交會被接受而不是拒絕。自 v5.10.11 起 POST /chatrooms/{cid}/quick-run 也會暫存 input,可能在 manager 檢查之後、建立任何任務或 run 之前回這個 |
| 422 | build_region_unavailable | POST .../versions/{vid}/builds:build_region 不在該部署公開的目錄內(託管雲端的 onprem;地端部署的任何雲端代碼),或地端已有 active 區域列、但指定的不在其中。什麼都沒排入 |
| 422 | bundle_path_forbidden | POST /tasks/{id}/bundle-uploads:封存檔根目錄含 runner 保留檔名(startup.sh、work.sh、driver.sh、input.json)、絕對路徑或磁碟機絕對路徑、含 NUL/反斜線的路徑;訊息會寫出是哪個條目(後端 ≥ #1140) |
| 409 | task_not_shared_to_room | POST /chatrooms/{cid}/granted-jobs/{task_id}/enable:任務存在,但沒有分享給這個聊天室(或其部門/公司);訊息會寫出該呼叫哪條分享 API。#1140 之前是單純的 404 |
| 409 | task_bundle_runner_unsupported | 發佈或執行帶 bundle 的任務版本,但環境的 runner release 未核准(版本上 runner_release_approved=false;後端 ≥ #1162 直接讀 operator 核准表)。解法:重新排一次環境建置(會帶上目前的 runner),或請 operator POST /root/sandbox/runner-releases。照 runner_release_next_action 做 |
| 422 | script_too_large/script_not_utf8/script_contains_nul | POST /tasks/{id}/script-uploads 或 POST .../versions/{vid}/script-file 的檔案有問題 |
| 422 | secret_slot_policies_invalid | POST /tasks/{id}/shares 的 secret_slot_policies map 格式不對(slot 名不合法、政策值不合法、或超過 100 筆) |
| 413 | request_too_large | 提交執行 body 依 Content-Length 過大 |
| 413 | context_too_large | Context PUT body/Content-Length 超過 declared_bytes |
| 400 | invalid_content_length | Content-Length header 不合法 |
| 409 | build_not_eligible | 版本 state 無法開始建置 |
| 409 | nonterminal_exists | 此版本已有未終態的建置 attempt |
| 409 | not_cancellable | 建置已終態。cancel_requested/cancelling 冪等 |
| 409 | retry_not_eligible | 重試一個不在 {completed, customer_failed, platform_failed, cancelled} 的 run,或 POST .../versions/{vid}/retry-build 時版本 state 不是 retryable_failed |
| 409 | retry_window_expired | 建置重試超過 7 天窗 |
| 409 | provisioning_retry_not_eligible | POST .../versions/{vid}/retry-provisioning:版本 state 不在 regional_provisioning/provisioning_blocked/ready |
| 409 | task_version_digest_mismatch | secret 授權的 digest 與已發布版本不符 |
| 409 | task_version_not_published | POST /secret-approvals(與別名 /shared-task-secret-approvals):task_version_id 指到的版本存在但不是 published。這道檢查在 digest 比對之前 |
| 409 | share_already_live | POST /tasks/{id}/shares:此 task + target 已經有一筆 live grant。要換 secret_slot_policies 必須先 revoke 再重新分享(新 generation) |
| 409 | policy_version_conflict | PUT /settings CAS 不符 |
| 409 | idempotency_conflict | 同一把 Idempotency-Key,不同 request hash |
| 409 | active_environment_limit | 公司撞上 active 環境上限(預設 30) |
| 429 | sandbox_queued_run_limit | 提交/重試(以及 Agent 與自訂表格 trigger 路徑)的公司 queued-run 上限:政策預設 100;地端部署取它與主機每公司上限(預設 20)中較小者。Quick Run 不看政策上限,但受地端上限約束。body detail = { code, message, limit }(SandboxQueuedRunLimitErrorResponse)。什麼都沒排入 |
| 429 | queue_full | 僅地端:整個 executor fleet 的佇列已達上限(預設 200)。body detail = { code, message, limit, retry_after_seconds }(SandboxQueueFullErrorResponse),另有 Retry-After: 60 header。什麼都沒排入——等過這段時間再重試 |
| 429 | staging_slot_exhausted | 20 個 live context-upload slot 或 20 GiB 宣告 staging。未完成上傳會漏 slot 直到 abort;連 scan report 寫入也會卡住 |
| 429 | upload_rate_limited | 每公司每分鐘超過 5 次 context-upload init |
| 409 | (非法狀態轉換 / CAS) | 發布/歸檔非法轉換 |
| 409 | content_scrubbed_irreversible/version_not_runnable | 提交執行時版本非 published+content active(_refuse_not_runnable):content_state=scrubbed 回 content_scrubbed_irreversible(永久不可執行);其他任何非 published+active 組合回 version_not_runnable |
| 409 | version_allocation_exhausted | 公司無法再分配一個 capacity-retained 版本(沒有已確認的區域配額/cap 為 0) |
| 409 | capability_fenced | 上傳 capability 對不上這個 session |
| 409 | output_policy_owner_scope_unsupported | 公司擁有的任務試圖發布 writeback 或 Command 輸出 policy |
| 409 | output_policy_unsatisfiable | 發布時,或提交且真的鑄出憑證時,writeback 表已不在/有 channel rule/是 commands_only;Command 輸出 policy 則是發布時任何一項 Command 檢查失敗(查找、輸入與模式、restricted definition authority 且有 effect_identity)——對發布者與已記錄撰寫者的身分、grant 與範圍檢查仍是 403 output_policy_author_denied |
| 503 | sandbox_disabled | kill switch 下的變更(SANDBOX_ENABLED=false;預設 true)。/cancel、/download 仍可用 |
| 503 | sandbox_secret_pepper_missing / sandbox_infisical_unconfigured | secret 寫入時 adapter/pepper 掛了 |
| 503 | capability_unavailable/storage_unavailable | 上傳 capability 或物件儲存掛了 |
| 503 | input_staging_unavailable | POST /chatrooms/{cid}/runs(自 v5.10.11 起還有 POST /chatrooms/{cid}/quick-run):暫存 input 的物件儲存不可用。手動提交在房間/版本授權之前暫存,Quick Run 在 manager 檢查之後;兩者都不會建出 run |
| 503 | build_admission_fenced | POST .../versions/{vid}/builds 與 POST .../versions/{vid}/retry-build:平台正在做 stage-image rollout,建置 admission 被全域圍欄擋住(數分鐘)。body 是 { code, message, retry_after_seconds },同值也放在 Retry-After header;沒有任何東西被佇列,照 Retry-After 重試即可 |
| 503 | sandbox_writeback_key_unavailable | 宣告了 policy 但 HMAC key 缺失/太短 |
Secret 傳輸加密錯誤(回應形狀不一樣)
POST /secrets 與 POST /secrets/rotate 用 pydantic model validator 驗證 encrypted_value,所以這四個不是本頁其他地方那種 {"detail": {"code": "..."}} 形狀——它們回的是 FastAPI 標準的驗證錯誤陣列,slug 在 detail[].type(也會鏡射進 detail[].msg,精確等於 validation_error:<slug>,input/ctx 會從回應裡剝除):
Slug(detail[].type) | 原因 |
|---|---|
sandbox_plaintext_value_rejected | 送了非空的明文 value 欄位(不論有沒有一起送 encrypted_value);空字串或 null 的 value 會被忽略,不算明文 |
sandbox_encrypted_value_required | encrypted_value 缺失或為 null |
sandbox_transit_keypair_missing | 伺服器沒設定解密金鑰對 |
sandbox_encrypted_value_undecryptable | 信封形狀合法但解密失敗 |
四個都是 HTTP 422。Client 端加密流程見 Secret 與授權。
不是 REST 錯誤:share_grant_revoked
share_grant_revoked(409)只會出現在 runner 呼叫的控制面 manifest 揭露路由上——絕不會出現在 /private/module/sandbox 的租戶端點。當一個 owner 出借的 secret pin,其分享 grant 在 run 認領之後、到後續某次 manifest 讀取之間死掉了(撤銷、重新分享成新 generation、或 authority 變動),就會觸發這個。前端不會直接收到這個代碼,只會看到對應的 run 以失敗終結。見 Secret 與授權。
不在 /private/module/sandbox 底下:command-output 端點(v5.21.0)
POST /public/module/custom_tables/callback/command-output/{token_id} 是 Command 輸出 run 的 work script 拿密封憑證去呼叫的公開端點。它回 {"detail": {"code": "..."}}:404 output_run_unavailable(憑證不存在,或 Bearer secret 錯誤/缺少)、409 output_run_unavailable(憑證已撤銷或過期、run 已不在進行中,或它的任務或版本不再是 active 且已發布)、409 output_command_stale、409 output_command_conflict、422 output_schema_violation、403 output_policy_author_denied;body 格式不對或 token_id 不是 UUID 時,則是 FastAPI 的 422 validation 陣列,Command 執行本身也可能帶來它自己的錯誤。各代碼的意義見任務輸出交給自訂表格 Command。
Agent 工具錯誤(非 REST)
五個工具回 { "status": "error", "error": "<code>", "message": "..." }:
error | 何時 |
|---|---|
unauthorized | 重新授權/擁有權(SandboxAgentAuthError),或提交遇到 401/403 拒絕(例如 Command 輸出的規則) |
validation_error | 參數不合法,或提交遇到 400/409/422 拒絕 |
forged_receipt | 收據不是簽過名的多段格式(必須含 4 個點) |
not_found | 過期/別人的/隱藏/同一輪/已用過的收據 |
confirmation_rejected | 其他閉形式確認失敗 |
already_committed | 提案已經變成 run |
proposal_not_pending | 狀態不是 prepared |
same_turn / not_later / receipt_reused | 同一輪、不夠晚、或確認輪次已用過 |
retryable | 暫時性 commit;提案留在 prepared |
confirmation_input_unavailable | 確認已驗證;耐久 input 無法重放。不建 run;提案仍 pending |
provider_unavailable | 控制面不可用,或工具沒有對應到其他代碼的提交拒絕——包括兩種 429(sandbox_queued_run_limit、地端的 queue_full) |
internal_error | 未預期;泛用訊息 |
前端處理建議
- 404:別假設是「不存在」——先確認權限、公司、key scope、id 是否屬於當前使用者。
- 403
owner_scope_forbidden:換一個呼叫者能擁有的 scope,或換有該 scope 管理權的人。 - 403
budget_reservation_exceeded:公司配額蓋不住這次 envelope;提示額度,不要重送同一請求。 - 422 缺 header:補
Idempotency-Key(每次新意圖用新 UUID,重試沿用)。 - 409:重新讀取資源目前 state 再決定下一步(別盲目重送)。
- 409
version_allocation_exhausted:沒有已確認的區域配額或 cap 為 0——這是營運/配額問題,不是租戶少填欄位。 - 503
sandbox_disabled:kill switch 關掉了(SANDBOX_ENABLED=false;預設開)。提示使用者稍後再試,取消/下載仍可用。 - 418:
Authorization與X-Api-Key都沒帶(共用 TeamSync credential 例外,Could not validate credentials,發生在 sandbox 404 隱藏之前)——不是 401;見認證。 - 租戶面的 429:
sandbox_queued_run_limit、queue_full(地端;遵守Retry-After)、staging_slot_exhausted、upload_rate_limited。退避;不要對 init 空轉重試。503 以外的 5xx 是平台失敗——退避重試;除非你還握著Idempotency-Key,否則不要假設 run 沒建出來。
v5.10.0 新增的驗證與重試情況
- 422
detail[].type = sandbox_value_too_short:create/rotate 解密後少於 4 個 UTF-8 bytes。 - 409
task_version_not_published:明確 secret 核准指向未發布版本。 - 409
retry_input_unavailable:無法重放保留的來源 input,請傳明確 input。 - 核准 slot 集合非法/空白,或作者宣告使用保留 slot 名稱時,回 422 驗證錯誤。
請使用目前 OpenAPI 的 wait_reason、error_code、failure_stage 與 failure_source 型別。Provider 拒絕在重試處理後仍保留實際且已遮罩的來源資訊,不要自行轉成沒有內容的成功狀態。