SIGN IN SIGN UP

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