Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,8 @@ jobs:
- name: Bootstrap patched pip trust
uses: durable-workflow/.github/.github/actions/bootstrap-patched-pip@75587fc33ff40c34f32ef777293dad56bb6f48c7 # clean-trust
- name: Install policy dependencies
run: python -m pip install PyYAML==6.0.2
run: >-
python -m pip install --only-binary :all: PyYAML==6.0.2
- name: Require centrally approved immutable action commits
run: >-
python .central-action-policy/scripts/qualification_policy.py validate
Expand Down Expand Up @@ -191,7 +192,8 @@ jobs:
uses: durable-workflow/.github/.github/actions/bootstrap-patched-pip@75587fc33ff40c34f32ef777293dad56bb6f48c7 # clean-trust
with:
clients: pip,python
- run: pip install build twine
- run: >-
pip install --only-binary :all: build==1.6.1 twine==7.0.0
- run: python -m build
- name: Compare built release metadata with the exact source commit
run: python scripts/check_release_metadata.py --source-ref "$(git rev-parse HEAD)" --dist dist
Expand Down
3 changes: 2 additions & 1 deletion .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,8 @@ jobs:
clients: pip,python

- name: Install build tools
run: pip install build twine
run: >-
pip install --only-binary :all: build==1.6.1 twine==7.0.0

- name: Build package
run: python -m build
Expand Down
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

## [2.17.1] - 2026-11-13

### Fixed

- `Client` refuses supplied `token`, `control_token` and `worker_token` values with edge whitespace or control characters before choosing a role or opening an HTTP client. The error names only the option. Values are never trimmed, and empty values retain their existing fallback behavior. (#304)
Expand Down
45 changes: 45 additions & 0 deletions docs/reference/client.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,50 @@
# Client

## Read an update outcome

`Client.update_workflow()` returns a Server receipt. Its default wait stage is
`accepted`: the handler may still be running when the call returns. Keep the
receipt's `update_id` to read the result later without submitting another update.

```python
from durable_workflow import UpdateStillPending

receipt = await client.update_workflow(
"order-1", "rename", args=["new name"], wait_for="accepted",
)
update_id = receipt["update_id"]

try:
result = await client.update_outcome("order-1", update_id, wait_timeout_seconds=30)
except UpdateStillPending as pending:
update_id = pending.update_id
print(f"Update {update_id} is still running; read its outcome again later.")
else:
print(result) # A completed handler may return None.
```

`update_outcome()` waits for completion by default. A pending update raises
`UpdateStillPending`, carrying the workflow, run, update and request identities.
It never returns pending work as `None`. To inspect the raw receipt without
waiting for completion, call `update_outcome_receipt()`; pass
`wait_for="completed"` to wait there too. Reads follow an update across
continue-as-new and never admit another update.

| Outcome | Result |
| --- | --- |
| Completed | The decoded handler result, including `None` |
| Still running | `UpdateStillPending`; keep its `update_id` and read again |
| Handler failed or validator rejected | `UpdateFailed` or `UpdateRejected` |
| No retained update with this id | `UpdateNotFound`, including after pruning |
| Response does not identify the requested update or a recognized status | `UnrecognizedUpdateOutcome` |

Both read methods require Server discovery to advertise
`capabilities.update_outcome_reads`. An unsupported Server raises
`RuntimeCapabilityUnsupported`; it does not fall back to resubmitting the update.
`WorkflowHandle` and `durable_workflow.sync.Client` provide the same methods.
See the [complete update reference](../sdk-reference.md#workflow-signals-queries-and-updates)
for wait stages, validation and response checks.

## Trust a private certificate authority

The `tls_context` option requires Python SDK 2.10.0 or later.
Expand Down
6 changes: 3 additions & 3 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"

[project]
name = "durable-workflow"
version = "2.17.0"
version = "2.17.1"
description = "Python client and worker SDK for Durable Workflow Cloud and self-hosted Server"
readme = "README.md"
requires-python = ">=3.10"
Expand Down Expand Up @@ -79,8 +79,8 @@ durable-workflow-replay-conformance = "durable_workflow.replay_conformance:main"
durable-workflow-workflow-updates-conformance = "durable_workflow.workflow_updates_conformance:main"

[tool.durable-workflow]
product-train = "2.17.0"
registry-version = "2.17.0"
product-train = "2.17.1"
registry-version = "2.17.1"
supported-server-versions = "2.8.0"
supported-server-release-baselines = { "2" = "2.8.0", "3" = "3.0.0" }
worker-protocol-version = "1.20"
Expand Down
Loading