@@ -303,7 +303,10 @@ private StartOperationResponse handleStartOperation(
303303 }
304304 // Convert each inbound nexus.v1.Link to common.v1.Link, dispatching on the link's
305305 // type field (WorkflowEvent, NexusOperation, etc.). LinkConverter logs the warn for
306- // any unknown type and returns null.
306+ // any unknown type and returns null. We don't throw as we know the URI is valid
307+ // (If it wasn't, then nexusProtoLinkToLink would have already thrown)
308+ // so this might just be a new link type and we don't want to
309+ // break forward compatibility.
307310 io .temporal .api .common .v1 .Link commonLink = LinkConverter .nexusLinkToCommonLink (link );
308311 if (commonLink != null ) {
309312 inboundCommonLinks .add (commonLink );
@@ -317,51 +320,57 @@ private StartOperationResponse handleStartOperation(
317320 StartOperationResponse .Builder startResponseBuilder = StartOperationResponse .newBuilder ();
318321 OperationContext context = ctx .build ();
319322 try {
320- OperationStartResult <HandlerResultContent > result =
321- startOperation (context , operationStartDetails .build (), input .build ());
322- // If signal or signalWithStart RPCs the handler issued returned backlinks, propagate them
323- // to the caller so the caller workflow's history event links to each event on the callee.
324- // Same set of backlinks applies to both sync and async response variants.
325- List <io .temporal .api .nexus .v1 .Link > backlinks = new ArrayList <>();
326- for (io .temporal .api .common .v1 .Link backlink :
327- CurrentNexusOperationContext .get ().getBacklinks ()) {
328- io .temporal .api .nexus .v1 .Link converted = LinkConverter .commonLinkToNexusLink (backlink );
329- if (converted != null ) {
330- backlinks .add (converted );
331- } else {
332- // The SDK stashed this backlink itself in RootWorkflowClientInvoker; failing to re-encode
333- // it now means a LinkConverter regression or a malformed link from the server. Either is
334- // an SDK invariant violation worth shouting about (warn is too quiet — the caller's
335- // history event will be missing a link with no other diagnostic).
336- log .error (
337- "SDK-stashed backlink failed to re-encode as nexus.v1.Link; caller history will be"
338- + " missing a link. backlink={}" ,
339- backlink );
323+ try {
324+ OperationStartResult <HandlerResultContent > result =
325+ startOperation (context , operationStartDetails .build (), input .build ());
326+ // If signal or signalWithStart RPCs the handler issued returned backlinks, propagate them
327+ // to the caller so the caller workflow's history event links to each event on the callee.
328+ // Same set of backlinks applies to both sync and async response variants.
329+ List <io .temporal .api .nexus .v1 .Link > backlinks = new ArrayList <>();
330+ for (io .temporal .api .common .v1 .Link backlink :
331+ CurrentNexusOperationContext .get ().getBacklinks ()) {
332+ io .temporal .api .nexus .v1 .Link converted = LinkConverter .commonLinkToNexusLink (backlink );
333+ if (converted != null ) {
334+ backlinks .add (converted );
335+ } else {
336+ // The SDK stashed this backlink itself in RootWorkflowClientInvoker; failing to
337+ // re-encode it now means a LinkConverter regression or a malformed link from the
338+ // server. Either is an SDK invariant violation worth shouting about (warn is too
339+ // quiet — the caller's history event will be missing a link with no other diagnostic).
340+ log .error (
341+ "SDK-stashed backlink failed to re-encode as nexus.v1.Link; caller history will be"
342+ + " missing a link. backlink={}" ,
343+ backlink );
344+ }
340345 }
341- }
342346
343- if (result .isSync ()) {
344- startResponseBuilder .setSyncSuccess (
345- StartOperationResponse .Sync .newBuilder ()
346- .setPayload (Payload .parseFrom (result .getSyncResult ().getDataBytes ()))
347- .addAllLinks (backlinks )
348- .build ());
349- } else {
350- startResponseBuilder .setAsyncSuccess (
351- StartOperationResponse .Async .newBuilder ()
352- .setOperationId (result .getAsyncOperationToken ())
353- .setOperationToken (result .getAsyncOperationToken ())
354- .addAllLinks (
355- context .getLinks ().stream ()
356- .map (
357- link ->
358- io .temporal .api .nexus .v1 .Link .newBuilder ()
359- .setType (link .getType ())
360- .setUrl (link .getUri ().toString ())
361- .build ())
362- .collect (Collectors .toList ()))
363- .addAllLinks (backlinks )
364- .build ());
347+ if (result .isSync ()) {
348+ startResponseBuilder .setSyncSuccess (
349+ StartOperationResponse .Sync .newBuilder ()
350+ .setPayload (Payload .parseFrom (result .getSyncResult ().getDataBytes ()))
351+ .addAllLinks (backlinks )
352+ .build ());
353+ } else {
354+ startResponseBuilder .setAsyncSuccess (
355+ StartOperationResponse .Async .newBuilder ()
356+ .setOperationId (result .getAsyncOperationToken ())
357+ .setOperationToken (result .getAsyncOperationToken ())
358+ .addAllLinks (
359+ context .getLinks ().stream ()
360+ .map (
361+ link ->
362+ io .temporal .api .nexus .v1 .Link .newBuilder ()
363+ .setType (link .getType ())
364+ .setUrl (link .getUri ().toString ())
365+ .build ())
366+ .collect (Collectors .toList ()))
367+ .addAllLinks (backlinks )
368+ .build ());
369+ }
370+ } catch (OperationException e ) {
371+ throw e ;
372+ } catch (Throwable failure ) {
373+ convertKnownFailures (failure );
365374 }
366375 } catch (OperationException e ) {
367376 TemporalFailure temporalFailure ;
0 commit comments