Report non-retryable PayloadValidationError as BadRequest (#840)
## What changed A payload codec or payload converter can now signal that a Nexus operation's input is invalid by throwing a non-retryable `ApplicationFailureException` with error type `PayloadValidationError` while decoding the input. Such a failure is translated into a non-retryable `BadRequest` `HandlerException` with the message `Invalid operation input`, retaining the original failure as its inner exception. Previously such a failure became an `Internal` handler exception on the codec path, which callers retry — so a caller sending invalid input was retried until timeout instead of failing fast. On the converter path the type was already `BadRequest`, but it shared the generic `Payload converter failed to decode Nexus operation input` message, so a validation rejection was indistinguishable from a genuine decode failure; it now gets its own message. ## Unchanged - `ApplicationFailureException` of any other error type → `Internal` on the codec path (as before) - A **retryable** `PayloadValidationError` → unchanged; non-retryable is required - `HandlerException` thrown by the codec/converter → passed through untouched - The generic decode-failure messages are untouched for non-validation failures - The serialize/output path is untouched — `BadRequest` would be wrong for a result-encoding failure ## Tests - `NexusPayloadSerializerTests.cs` (new): 10 server-independent cases covering both the codec and converter stages — positive cases asserting `BadRequest`, `NonRetryable`, the wrapper message, and that the inner exception is an `ApplicationFailureException` with its message and `ErrorType` preserved; plus a `[Theory]` over the negative combinations (retryable + validation type, non-retryable + other type, retryable + other type, null type) asserting the failure propagates unwrapped. - `NexusWorkerTests.cs`: functional coverage against a real dev server for the converter path, asserting the caller sees a non-retryable `BadRequest` with an `ApplicationFailureException` cause, and a negative test that another error type still yields `Internal`. ## Cross-SDK Part of a coordinated change; equivalent PRs exist for Go, Java, TypeScript and Python. The wrapper message wording is aligned across SDKs, adapted to each SDK's message style.
R
Roey Berman committed
c22f24d373f5168ac0c6f07fb772b60ee4ee9281
Parent: 5f0c518
Committed by GitHub <noreply@github.com>
on 8/17/2026, 10:05:29 PM