You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
POST /api/v1/approvals/requests/{id}/reject returns 500 while its effect lands AND strands the workflow run — three inconsistent outcomes from one call #13807
Split out of #13568 by the triage seat. #13568 was ruled by the maintainer at 2026-08-31T02:20Z (director batch #5, 「同意」). The evidence below was added to that card at 02:54Z and 03:57Z — after the ruling — and describes a defect the ruling does not reach: #13568's remedy (auto-cancel pending approvals when a record is deleted) prevents future orphans; it says nothing about a write path that returns an error while applying its effect, and nothing about the runs already stranded.
The measurement (from #13568's follow-up comments, reporter yinlianghui, re-read from transcript by the filing session — not recalled)
One call against an orphaned sys_approval_request (its record_id points at a deleted crm_leave_request row):
POST /api/v1/approvals/requests/{approvalRequestId}/reject body {"comment": "<text>"}
→ HTTP 500
and afterwards three states coexist:
the caller received 500;
the request's statusisrejected, and it has disappeared from the pending inbox — the effect landed;
the workflow run is stranded — unrecoverable.
The full error text (the first quote of it in #13568 was truncated; the reporter corrected it in the third comment, which is the version to trust):
…ow run 'run_8380f743-ac5b-4c27-a976-9ac5e71f6a00' could not be resumed and is
now stranded: resume of run 'run_8380f743-ac5b-4c27-a976-9ac5e71f6a00' failed:
Node 'mark_rejected' failed: update_record(crm_leave_request) failed:
Record 9SEmlyRfw8D9-J7Z not found in crm_leave_request
Controls from the same session: POST …/approve on a healthy request → 200. DELETE /api/v1/data/sys_approval_request/{id} → 405 (so the orphan cannot be cleaned up through the data API either).
The 500 lies about the outcome. A caller — human, script, or agent — reads 500 as "the rejection did not happen" and retries or escalates. It did happen. This is the error/effect divergence class, and it is worse than a plain failure because the honest-looking response is the wrong one.
The trigger generalises past approvals.mark_rejected fails because a workflow node writes back to a record that no longer exists. Any workflow node doing update_record on a since-deleted row plausibly reaches the same strand. ⚠️ Not measured — that generalisation is a hypothesis, and establishing its reach is this card's first step, not its premise.
First deliverables, in order
Establish the reach. Does any workflow node failing mid-resume strand the run, or is this specific to the approval resume path? That decides whether the fix is in plugin-approvals or in the workflow engine, and it may re-anchor this card's lane.
Decide the atomicity posture for the reject door: either the status write and the workflow resume succeed together, or neither does. ⛔ Returning 500 after a committed status write is not an option to preserve.
An operator path out of stranded — even if it is only a documented manual step. A state a deployment can enter and never leave is not acceptable as an end state, and the 405 on the data API means there is currently no way out at all.
Split out of #13568 by the triage seat. #13568 was ruled by the maintainer at 2026-08-31T02:20Z (director batch #5, 「同意」). The evidence below was added to that card at 02:54Z and 03:57Z — after the ruling — and describes a defect the ruling does not reach: #13568's remedy (auto-cancel pending approvals when a record is deleted) prevents future orphans; it says nothing about a write path that returns an error while applying its effect, and nothing about the runs already stranded.
The measurement (from #13568's follow-up comments, reporter
yinlianghui, re-read from transcript by the filing session — not recalled)One call against an orphaned
sys_approval_request(itsrecord_idpoints at a deletedcrm_leave_requestrow):and afterwards three states coexist:
statusisrejected, and it has disappeared from the pending inbox — the effect landed;The full error text (the first quote of it in #13568 was truncated; the reporter corrected it in the third comment, which is the version to trust):
Controls from the same session:
POST …/approveon a healthy request → 200.DELETE /api/v1/data/sys_approval_request/{id}→ 405 (so the orphan cannot be cleaned up through the data API either).Why this is p1 and separate from #13568
strandedis unrecoverable and has no operator story on this card.[finding] 删除记录后,它的审批单仍留在审批收件箱:pending 与 approved 两种都留,记录引用退化为原始 id #13568's ruling produces no mechanism for runs already in that state, because it did not know about them.mark_rejectedfails because a workflow node writes back to a record that no longer exists. Any workflow node doingupdate_recordon a since-deleted row plausibly reaches the same strand.First deliverables, in order
plugin-approvalsor in the workflow engine, and it may re-anchor this card's lane.stranded— even if it is only a documented manual step. A state a deployment can enter and never leave is not acceptable as an end state, and the 405 on the data API means there is currently no way out at all.⛔ Boundaries
/rejectreturn 200. The effect landing while a run strands is the defect; a green status code would only hide it.lockRecordediting bind ([finding] 删除记录后,它的审批单仍留在审批收件箱:pending 与 approved 两种都留,记录引用退化为原始 id #13568 item 4, "改不了只能删重建") is the amplifier that produces orphans in the first place, and the ruling explicitly declines to solve it here. Same posture on this card.Related
not_planned.