Repository navigation
Return pending updates as UpdateStillPending and read outcomes later by id - #243
Conversation
…by id (#212) update_workflow returned the Server's result: null for a 202 wait_timed_out receipt, so an update still running looked like a handler that returned null. A pending receipt now returns Error::UpdateStillPending with the update id, request id, current run and full receipt; a real null result is still Ok(Null). Client::update_outcome, update_outcome_avro_value and update_outcome_receipt (and WorkflowHandle::update_outcome) call GET /workflows/{id}/updates/{update_id} (server#1131) on a Server that advertises update_outcome_reads. Without the capability they return Error::UpdateOutcomeReadsUnavailable before reading anything and never resubmit the update. Error::is_update_not_found detects 404 update_not_found. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Security: CHANGES REQUESTED at 83252cd. A small fix: a confused read can come back as success.
Requested:
Everything else passes:
Separate, outside this PR: |
…ds (#212) Security review of #243: an update id of "." or ".." collapsed the read onto the workflow describe route, and any 200 body without update_status was treated as finished, so update_outcome could return the workflow's result or Null for an update it never read. Workflow and update ids now go through RawRequest's segment check and an empty, "." or ".." id is refused before any request. A successful answer must be a receipt for the requested update (matching update_id, an update_status, and the workflow id when echoed), and update_outcome only returns a value from a completed receipt; anything else is Error::UnrecognizedUpdateOutcome. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Security PASS at c4c96e1, on green CI. This resolves my change request. Both ids now go through |
# Conflicts: # CHANGELOG.md
|



Part of durable-workflow/.github#212 (Rust slice). Uses the read route from server#1131 / server#1155 (native 3.6.0), with the same semantics as sdk-php 3.15.0's
updateOutcome().Bug:
update_workflowwaits for completion. When the Server's wait ran out first (HTTP 202,wait_timed_out: true,result: null), it returnedOk(Null). That looks exactly like a handler that returned null.Change
Error::UpdateStillPending(UpdatePending)withupdate_id,request_id, currentrun_id,update_status,wait_timed_outand the full receipt. The message says how to read it again by update id, or how to resubmit with the samerequest_idon an older Server. A receipt with aresult_envelopeis finished, so an encoded null is stillOk(Null).update_workflow_avro_valuereturns the same error where it used to return amissing_payload_envelopecodec error.Client::update_outcome/update_outcome_avro_value(wait for completion, decode the result) andupdate_outcome_receipt(…, UpdateWaitStage, …)(raw receipt), plusWorkflowHandle::update_outcome[_avro_value]. Each is aGET /workflows/{id}/updates/{update_id}with optionalwait_for=completedandwait_timeout_seconds. The transport timeout is extended to cover the wait.capabilities.update_outcome_reads: true, and the client caches it once seen. Otherwise these calls returnError::UpdateOutcomeReadsUnavailableafter discovery, without reading anything, and the update is never resubmitted.Error::Http422 with the failure receipt, the same asupdate_workflow. A rejected update keeps its original status. NewError::is_update_not_found()detects 404update_not_found./workflows/{workflow_id}/updates/{update_id}. README section with an example (compiled as a doctest), plus a CHANGELOG entry under Unreleased.Behaviour change: callers that got
Ok(Null)for a timed-out update now getError::UpdateStillPending. The CHANGELOG notes this.Tests (
src/update_outcome/tests.rs, mocked HTTP): completed value and completed null; typed Avro read; pending read, which raises a typed error with a hint, followed by a re-read that returns the result, all over GET; accepted receipt read; failed 422; 404update_not_found; an older Server without the capability, which gets only discovery and no read; an empty update id; a submitted update that times out, which is now pending rather than null for both untyped and typed calls; a submitted update that returned null.Local (rust 1.86.0 in Docker):
cargo fmt --all --check,cargo test --locked --all-targets --all-features(986 passed, 0 failed), doctests, andRUSTDOCFLAGS=-D warnings cargo doc --all-features --no-depsall clean.Not covered here: an integration test against a published Server image. #212's acceptance asks for one, and it needs a published native 3.6.0+ image in the qualification jobs.
🤖 Generated with Claude Code