Skip to content

Propagate Nexus serialization context to workflows and standalone activities - #3171

Open
JoshuaFrenchwood wants to merge 9 commits into
mainfrom
nexus-serialization-context-updates
Open

JoshuaFrenchwood wants to merge 9 commits into
mainfrom
nexus-serialization-context-updates

Conversation

@JoshuaFrenchwood

@JoshuaFrenchwood JoshuaFrenchwood commented Oct 27, 2026 •

Copy link
Copy Markdown
Contributor

What was changed

Nexus operation handlers now propagate their NexusSerializationContext to the workflows and
standalone activities (SAA) they start.

  • Workflows: start and update-with-start send the handler's context
    (propagated_nexus_serialization_context). The worker encodes the workflow's result and closing
    failure with it. Clients decode the result with the context the server returns in the history,
    including detached handles and runs after continue-as-new.
  • Standalone activities: the start request carries the context. The worker encodes the result,
    failure and last-attempt failure with it. Get-result and describe decode those with the context
    the server returns.
  • Standalone Nexus operations: handles prefer the context returned by the server when polling,
    and fall back to the start request's context on older servers.
  • Everything else keeps the workflow or activity context: inputs, memo, signals, queries,
    heartbeat details, and static summary and details.
  • Pins the dev server used in tests to 1.9.3-nexus-serialization-context.

Why?

A Nexus handler returns the result and failure of the execution it starts to the Nexus caller. The
caller decodes them with the operation's NexusSerializationContext. Until now the backing
execution encoded them with its own workflow or activity context, so a codec keyed on the context
couldn't decode them. Per-endpoint encryption is one example.

Checklist

  1. Closes

  2. How was this tested:

  • Unit tests for the client invokers (start, get-result, poll, describe), for activity task
    execution, and for workflow replay with an injected propagated context.
    • End-to-end NexusSerializationContextPropagationTest on the dev server: a caller workflow goes
      through a Nexus endpoint to a handler that starts a workflow, a continue-as-new workflow or an
      SAA. It checks history payload contexts, describe, and handles obtained by ID.
    • StandaloneNexusSerializationContextTest against an external server, for standalone Nexus
      operations.
  1. Any docs updates needed?

@JoshuaFrenchwood
JoshuaFrenchwood requested a review from a team as a code owner October 27, 2026 19:14
Newer servers convert a Nexus operation error sent in the old failure
format to the new format, wrapping the handler's failure in an
OperationError. Unwrap it whenever it is present, so the old-format
assertions hold against both the pinned and older servers.
@JoshuaFrenchwood
JoshuaFrenchwood force-pushed the nexus-serialization-context-updates branch from 1fcabb1 to 512ff57 Compare October 27, 2026 20:45
@JoshuaFrenchwood JoshuaFrenchwood changed the title Nexus serialization context for Async Nexus Operations Propagate Nexus serialization context to workflows and standalone activities Oct 27, 2026
Drop tests whose code path another test already covers: the async
result and failure decodes that share their converter with the sync
paths, the start-before-operation edge case, and the end-to-end
workflow failure case covered by the replay and client unit tests.

@Evanthx Evanthx left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Had a few questions!

response.getInfo().hasPropagatedNexusSerializationContext()
? dataConverter.withContext(
PropagatedNexusSerializationContext.fromProto(
response.getInfo().getPropagatedNexusSerializationContext()))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fromProto requires a non-null, but getPropagatedNexusSerializationContext can return a null. Might want to make fromProto check for a null argument.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have a check with hasPropagatedNexusSerializationContext() before calling fromProto to account for this. Also the protobuf getter doesnt returns null, it returns an empty default instance when the field isn't set.

workflowStartInput.getWorkflowId(), null, workflowStartInput.getWorkflowType());
StartWorkflowExecutionRequestOrBuilder startRequest =
toStartRequest(dataConverterWithWorkflowContext, workflowStartInput);
toStartRequest(dataConverterWithWorkflowContext, workflowStartInput, null);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is passing a null for the Nexus context - is that as desired?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes this is signalWithStart, so that is intentional. NexusSerializationContext is only used for results/failures back to the Nexus caller. Signal has no result, since its fire and forget. For signalWithStart, the workflow result also does not go back to the nexus caller

@VegetarianOrc VegetarianOrc left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall looking correct to me!

One thing I think we're missing here is threading the correct context to manual activity completion. Right now looks like manual activity completion will encode the result w/ the activity context and then be forwarded to the Nexus Operation result.

Comment on lines +385 to 399
/**
* The data converter for the result or failure in a poll response. An activity started from a
* Nexus operation handler uses the handler's Nexus context, which the server returns with the
* response; other activities use an activity context.
*/
private DataConverter resultDataConverter(PollActivityExecutionResponse response) {
SerializationContext context =
response.hasPropagatedNexusSerializationContext()
? PropagatedNexusSerializationContext.fromProto(
response.getPropagatedNexusSerializationContext())
// Currently, result doesn't have access to activity type and serialization context
// doesn't hold activity ID.
: new ActivitySerializationContext(namespace, null, null, null, null, false);
return dataConverter.withContext(context);
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Codex is pointing out an edge case in how we're handling heartbeat details in the event of a timeout that I think is real and worth considering.

  1. Activity heartbeats and details encoded w/ activity context.
  2. Activity stops heartbeating
  3. Server times out the activity and embeds the activity-encoded last heartbeat details into the failure proto
  4. Client selects the nexus context to decode the failure. A custom codec may fail to decode the failure proto.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ahh yes that needs a fix, working on it now thanks.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated this. For activity heartbeats, they now use NexusSerializationContext to encode details when started by a nexus caller.

Also included a fix for async completion of an SAA with NexusSerializationContext.

JoshuaFrenchwood and others added 3 commits October 28, 2026 10:35
An activity completed through local manual completion reports its result,
failure and heartbeats through a client the worker creates. For a
standalone activity started from a Nexus operation handler, give that
client the handler's Nexus context, matching the worker's own completion
and heartbeat paths.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants