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
{{ message }}
Repository navigation
Native: deliver typed refusals for oversized workflow-task completions #1136
Server slice of durable-workflow/.github#228. Native caps the whole request body for workflow-task completions (payload_limits::enforce), so a large fan-out is refused even when each command is within limits. Next: accept completions within per-command limits and refuse the rest with a typed structural_limit_exceeded, with the encoded-vs-raw size table Workflow asked for. Owner: server.
changed the title [-]Native: large workflow-task completions hit a whole-body 413 (.github#228)[/-][+]Native: deliver typed refusals for oversized workflow-task completions[/+]on Nov 13, 2026
Reopening on published-artifact evidence from the Python installed run. Owner: server, scheduled immediately after the #934/3.9.1 correction; Python owns installed requalification, with PHP/Rust repeats coordinated through the shared issue.
The ordinary 300 × 10 KB fan-out passed on native 3.8.0. Aggregate over-limit completions did not receive the typed refusal: published Python SDK 2.17.0 sees httpx.ReadError(''), retries, and leaves the run pending/repaired. A synthetic ~36 MB JSON body to the completion route reproduces the missing HTTP 413 without a real task. The same middleware that enforces this issue's aggregate limit rejects an over-limit Content-Length before consuming the body, so the transport can reset while the client is still sending.
Remaining acceptance: deliver a bounded, reliable HTTP 413 with payload_too_large / workflow_task_completion_bytes to actual clients; make the installed worker record the intended one-time structural failure instead of retrying forever; preserve under-limit and per-payload controls. Cover fixed Content-Length and streamed bodies and keep body consumption/resource budgets bounded. Source/router tests alone do not close this: repeat published PHP/Python/Rust over-limit acceptance and link the exact corrected Server image and results.
No new PHP Server release is planned. The native response-delivery correction belongs here rather than a duplicate issue.
PHP's installed Worker follow-through found a separate planning gap in this same contract: SDK 3.17 applies discovery's per-payload limits.max_payload_bytes to the aggregate workflow completion, so a valid 300 × 10 KB fan-out can demand external storage even though native's completion body cap permits it. PHP's direct HTTP transport does receive the oversized 413; Python/Rust still need the response-delivery correction.
Agreed with the PHP owner: Server will add limits.max_workflow_task_completion_bytes, exposing the exact configured completion-body cap used by middleware (currently 16 times max_payload_bytes, with the same overflow handling). It does not change the per-command payload limit. Server owns discovery, response delivery, docs and tests after #934; PHP owns route-specific aggregate planning and its regression/installed repeat. Consumers of an older producer without the field need an explicit conservative fallback; PHP Server must not be assumed to have native's aggregate allowance. Python/Rust installed over-limit repeats remain required through the shared issue.
PHP consumer source is draft sdk-php#406. It uses the confirmed limits.max_workflow_task_completion_bytes only on completion, keeps ordinary/per-value limits and older-producer fallback, and permits one inline submission only with explicitly unconfigured/disabled/unsupported storage and individually valid encoded values. Configured-storage transients remain refusals. Unit vectors cover 1100-byte ordinary limit with 1500/3000-byte completion limits, typed413 actual HTTP byte counts, no upload/retry, cache isolation, malformed limits and exact whole-float serialization. All117 upload cases/473 assertions and PHPStan pass; seven core controls fail on the old planner. Server producer publication + real installed Worker within/above-limit acceptance remain required.
Planner contract agreed with Server: limits.max_workflow_task_completion_bytes is an optional positive integer wire-request cap, used only for workflow-task completion and activity continuation completion. Honor a smaller explicit cap too. Absent retains ordinary-cap fallback; never infer a version or 16× multiplier. Rust's existing strict discovery parser will reject explicit malformed/null/zero/negative fields with external_payload_unsupported before any upload/submission; coordinating the same shared vectors with PHP.
When storage is unavailable and each inline value satisfies the existing per-value/threshold rules, preserve the whole atomic inline completion. If it exceeds the advertised aggregate cap, send it once and retain the Server's authoritative typed workflow_task_completion_bytes refusal. Do not synthesize an HTTP status or turn an aggregate limit into a demand for unavailable external storage. Ordinary requests and individual values retain their existing limits.
Rust implementation owner: sdk-rust, #254. Source tests will cover both routes, unsupported/invalid producers, per-value/ordinary isolation, no partial uploads and actual typed HTTP 413 handling. Published fan-out and over-limit acceptance remain outstanding until the corrected published tuple is available.
Implementation draft: #1203, exact6a7d5972. It adds bounded remaining-body discard before typed413 delivery, advertises the actual completion wire cap from the same enforcement helper, and supplies portable exact/over-cap vectors for ordinary and continuation completion routes. Six focused source transport tests, discovery source test and28 comparator checks pass. Server owns source/Security/CI/publication; PHP and Rust owners implement scoped optional-field planning; PHP/Python/Rust owners repeat actual installed within/over-cap workers after publication. No published qualification claim and no shared#228 closure yet.
Release next action (Server owner): finish the independently gated legacy-key3.9.1 source/release first (#1202 merge queue; #1205 release prep), then publish #1203 as the proposed immutable3.9.2 completion correction with unchanged3.9 schema. General and Waterline have the ordering so next-schema #1200 stays draft until these correctness patches are tagged. #1203 exact919b0f08 has Security source/authority PASS; required CI is still pending. PHP and Rust own scoped planner packages, and PHP/Python/Rust own actual installed within/over-cap worker repeats on the qualified published tuple. SDK releases and shared#228 closure remain held for that evidence.
General selected one combined immutable3.9.1 correction cohort for #1202 and #1203. The prior proposed3.9.2 split is superseded; no3.9.1 tag or immutable publication has started. Server now refreshes #1203 onto accepted #1202 main, reruns combined normal gates and Security counter-review, then refreshes #1205 notes against that combined source before tagging/publishing/qualifying3.9.1. Both fixes preserve the3.9 physical schema. #1200 stays draft until this patch tag. PHP/Python/Rust installed within/over acceptance and shared#228 closure remain outstanding until the exact published producer/SDK tuple qualifies.
#1203 and the final combined notes/Compose preparation #1205 are merged. Immutable 3.9.1 now names accepted source 7d7ad9d6c9aee2e4592d74389ba9efd36e741f44; publication and per-architecture scans are running in 43423285947. The actual release tree exactly equals Security-reviewed 34a4e2ff after normal required checks and merge queue. The #934 correction shares this same cohort; no separate 3.9.2 publication is planned.
Next owner server: qualify the new exact published index with the native corpus and all four Compose cells, then complete protected promotion/final Release. PHP, Python and Rust owners already own their actual installed within-cap and oversized Worker repeats under shared #228; I will send the exact version/source/index/platform tuple when qualified. This issue and #228 remain open: source TCP controls, an image tag and small fan-out alone do not establish installed structural-failure/follow-up acceptance. Old 3.9.0 aliases/final Release stay held; #1194/#1204 remain separate reliability records.
Activity
Accept large fan-out completions within per-command limits
Merge pull request durable-workflow#1152 from durable-workflow/fix/se…
[-]Native: large workflow-task completions hit a whole-body 413 (.github#228)[/-][+]Native: deliver typed refusals for oversized workflow-task completions[/+]rmcdaniel commentedon Nov 13, 2026
Reopening on published-artifact evidence from the Python installed run. Owner: server, scheduled immediately after the #934/3.9.1 correction; Python owns installed requalification, with PHP/Rust repeats coordinated through the shared issue.
The ordinary 300 × 10 KB fan-out passed on native 3.8.0. Aggregate over-limit completions did not receive the typed refusal: published Python SDK 2.17.0 sees
httpx.ReadError(''), retries, and leaves the run pending/repaired. A synthetic ~36 MB JSON body to the completion route reproduces the missing HTTP 413 without a real task. The same middleware that enforces this issue's aggregate limit rejects an over-limit Content-Length before consuming the body, so the transport can reset while the client is still sending.Remaining acceptance: deliver a bounded, reliable HTTP 413 with
payload_too_large/workflow_task_completion_bytesto actual clients; make the installed worker record the intended one-time structural failure instead of retrying forever; preserve under-limit and per-payload controls. Cover fixed Content-Length and streamed bodies and keep body consumption/resource budgets bounded. Source/router tests alone do not close this: repeat published PHP/Python/Rust over-limit acceptance and link the exact corrected Server image and results.No new PHP Server release is planned. The native response-delivery correction belongs here rather than a duplicate issue.
rmcdaniel commentedon Nov 13, 2026
PHP's installed Worker follow-through found a separate planning gap in this same contract: SDK 3.17 applies discovery's per-payload
limits.max_payload_bytesto the aggregate workflow completion, so a valid 300 × 10 KB fan-out can demand external storage even though native's completion body cap permits it. PHP's direct HTTP transport does receive the oversized 413; Python/Rust still need the response-delivery correction.Agreed with the PHP owner: Server will add
limits.max_workflow_task_completion_bytes, exposing the exact configured completion-body cap used by middleware (currently 16 timesmax_payload_bytes, with the same overflow handling). It does not change the per-command payload limit. Server owns discovery, response delivery, docs and tests after #934; PHP owns route-specific aggregate planning and its regression/installed repeat. Consumers of an older producer without the field need an explicit conservative fallback; PHP Server must not be assumed to have native's aggregate allowance. Python/Rust installed over-limit repeats remain required through the shared issue.rmcdaniel commentedon Nov 13, 2026
PHP consumer source is draft sdk-php#406. It uses the confirmed limits.max_workflow_task_completion_bytes only on completion, keeps ordinary/per-value limits and older-producer fallback, and permits one inline submission only with explicitly unconfigured/disabled/unsupported storage and individually valid encoded values. Configured-storage transients remain refusals. Unit vectors cover 1100-byte ordinary limit with 1500/3000-byte completion limits, typed413 actual HTTP byte counts, no upload/retry, cache isolation, malformed limits and exact whole-float serialization. All117 upload cases/473 assertions and PHPStan pass; seven core controls fail on the old planner. Server producer publication + real installed Worker within/above-limit acceptance remain required.
rmcdaniel commentedon Nov 13, 2026
Planner contract agreed with Server:
limits.max_workflow_task_completion_bytesis an optional positive integer wire-request cap, used only for workflow-task completion and activity continuation completion. Honor a smaller explicit cap too. Absent retains ordinary-cap fallback; never infer a version or 16× multiplier. Rust's existing strict discovery parser will reject explicit malformed/null/zero/negative fields withexternal_payload_unsupportedbefore any upload/submission; coordinating the same shared vectors with PHP.When storage is unavailable and each inline value satisfies the existing per-value/threshold rules, preserve the whole atomic inline completion. If it exceeds the advertised aggregate cap, send it once and retain the Server's authoritative typed
workflow_task_completion_bytesrefusal. Do not synthesize an HTTP status or turn an aggregate limit into a demand for unavailable external storage. Ordinary requests and individual values retain their existing limits.Rust implementation owner: sdk-rust, #254. Source tests will cover both routes, unsupported/invalid producers, per-value/ordinary isolation, no partial uploads and actual typed HTTP 413 handling. Published fan-out and over-limit acceptance remain outstanding until the corrected published tuple is available.
rmcdaniel commentedon Nov 13, 2026
Implementation draft: #1203, exact6a7d5972. It adds bounded remaining-body discard before typed413 delivery, advertises the actual completion wire cap from the same enforcement helper, and supplies portable exact/over-cap vectors for ordinary and continuation completion routes. Six focused source transport tests, discovery source test and28 comparator checks pass. Server owns source/Security/CI/publication; PHP and Rust owners implement scoped optional-field planning; PHP/Python/Rust owners repeat actual installed within/over-cap workers after publication. No published qualification claim and no shared#228 closure yet.
rmcdaniel commentedon Nov 13, 2026
Release next action (Server owner): finish the independently gated legacy-key3.9.1 source/release first (#1202 merge queue; #1205 release prep), then publish #1203 as the proposed immutable3.9.2 completion correction with unchanged3.9 schema. General and Waterline have the ordering so next-schema #1200 stays draft until these correctness patches are tagged. #1203 exact919b0f08 has Security source/authority PASS; required CI is still pending. PHP and Rust own scoped planner packages, and PHP/Python/Rust own actual installed within/over-cap worker repeats on the qualified published tuple. SDK releases and shared#228 closure remain held for that evidence.
rmcdaniel commentedon Nov 13, 2026
General selected one combined immutable3.9.1 correction cohort for #1202 and #1203. The prior proposed3.9.2 split is superseded; no3.9.1 tag or immutable publication has started. Server now refreshes #1203 onto accepted #1202 main, reruns combined normal gates and Security counter-review, then refreshes #1205 notes against that combined source before tagging/publishing/qualifying3.9.1. Both fixes preserve the3.9 physical schema. #1200 stays draft until this patch tag. PHP/Python/Rust installed within/over acceptance and shared#228 closure remain outstanding until the exact published producer/SDK tuple qualifies.
rmcdaniel commentedon Nov 13, 2026
#1203 and the final combined notes/Compose preparation #1205 are merged. Immutable 3.9.1 now names accepted source
7d7ad9d6c9aee2e4592d74389ba9efd36e741f44; publication and per-architecture scans are running in 43423285947. The actual release tree exactly equals Security-reviewed34a4e2ffafter normal required checks and merge queue. The #934 correction shares this same cohort; no separate 3.9.2 publication is planned.Next owner server: qualify the new exact published index with the native corpus and all four Compose cells, then complete protected promotion/final Release. PHP, Python and Rust owners already own their actual installed within-cap and oversized Worker repeats under shared #228; I will send the exact version/source/index/platform tuple when qualified. This issue and #228 remain open: source TCP controls, an image tag and small fan-out alone do not establish installed structural-failure/follow-up acceptance. Old 3.9.0 aliases/final Release stay held; #1194/#1204 remain separate reliability records.