Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 49 additions & 1 deletion openapi/openapiv2.json
Original file line number Diff line number Diff line change
Expand Up @@ -9252,6 +9252,12 @@
},
"nexusHandlerFailureInfo": {
"$ref": "#/definitions/v1NexusHandlerFailureInfo"
},
"nexusSdkOperationFailureInfo": {
"$ref": "#/definitions/v1NexusSDKOperationFailureInfo"
},
"nexusSdkFailureErrorInfo": {
"$ref": "#/definitions/v1NexusSDKFailureErrorFailureInfo"
}
}
},
Expand Down Expand Up @@ -9302,6 +9308,9 @@
"format": "date-time",
"description": "The timestamp when the request was scheduled in the frontend."
},
"capabilities": {
"$ref": "#/definitions/v1RequestCapabilities"
},
"startOperation": {
"$ref": "#/definitions/v1StartOperationRequest"
},
Expand Down Expand Up @@ -12700,7 +12709,8 @@
"type": "string",
"description": "Operation token - may be empty if the operation completed synchronously."
}
}
},
"description": "Representation of the Temporal SDK NexusOperationError object that is returned to workflow callers."
},
"v1NexusOperationScheduledEventAttributes": {
"type": "object",
Expand Down Expand Up @@ -12790,6 +12800,31 @@
},
"description": "Nexus operation timed out."
},
"v1NexusSDKFailureErrorFailureInfo": {
"type": "object",
"properties": {
"metadata": {
"type": "object",
"additionalProperties": {
"type": "string"
}
},
"data": {
"type": "string",
"format": "byte"
}
},
"description": "Representation of the Nexus SDK FailureError object."
},
"v1NexusSDKOperationFailureInfo": {
"type": "object",
"properties": {
"state": {
"type": "string"
}
},
"description": "Representation of the Nexus SDK OperationError object."
},
"v1OnConflictOptions": {
"type": "object",
"properties": {
Expand Down Expand Up @@ -13756,6 +13791,15 @@
"v1RequestCancelWorkflowExecutionResponse": {
"type": "object"
},
"v1RequestCapabilities": {
"type": "object",
"properties": {
"temporalFailureResponses": {
"type": "boolean",
"description": "If set, handlers may use temporal.api.failure.v1.Failure instances to return failures to the server.\nThis also allows handler and operation errors to have their own messages and stack traces."
}
}
},
"v1RequestIdInfo": {
"type": "object",
"properties": {
Expand Down Expand Up @@ -14984,6 +15028,10 @@
"operationError": {
"$ref": "#/definitions/v1UnsuccessfulOperationError",
"description": "The operation completed unsuccessfully (failed or canceled)."
},
"failure": {
"$ref": "#/definitions/apifailurev1Failure",
"description": "The operation completed unsuccessfully (failed or canceled).\nFailure object must contain a NexusSDKOperationFailureInfo object."
}
},
"description": "Response variant for StartOperationRequest."
Expand Down
22 changes: 22 additions & 0 deletions openapi/openapiv3.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8337,6 +8337,10 @@ components:
$ref: '#/components/schemas/NexusOperationFailureInfo'
nexusHandlerFailureInfo:
$ref: '#/components/schemas/NexusHandlerFailureInfo'
nexusSdkOperationFailureInfo:
$ref: '#/components/schemas/NexusSDKOperationFailureInfo'
nexusSdkFailureErrorInfo:
$ref: '#/components/schemas/NexusSDKFailureErrorFailureInfo'
FetchWorkerConfigRequest:
type: object
properties:
Expand Down Expand Up @@ -9459,6 +9463,7 @@ components:
operationToken:
type: string
description: Operation token - may be empty if the operation completed synchronously.
description: Representation of the Temporal SDK NexusOperationError object that is returned to workflow callers.
NexusOperationScheduledEventAttributes:
type: object
properties:
Expand Down Expand Up @@ -9550,6 +9555,23 @@ components:
type: string
description: The request ID allocated at schedule time.
description: Nexus operation timed out.
NexusSDKFailureErrorFailureInfo:
type: object
properties:
metadata:
type: object
additionalProperties:
type: string
data:
type: string
format: bytes
description: Representation of the Nexus SDK FailureError object.
NexusSDKOperationFailureInfo:
type: object
properties:
state:
type: string
description: Representation of the Nexus SDK OperationError object.
OnConflictOptions:
type: object
properties:
Expand Down
14 changes: 14 additions & 0 deletions temporal/api/failure/v1/message.proto
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ message ChildWorkflowExecutionFailureInfo {
temporal.api.enums.v1.RetryState retry_state = 6;
}

// Representation of the Temporal SDK NexusOperationError object that is returned to workflow callers.
message NexusOperationFailureInfo {
// The NexusOperationScheduled event ID.
int64 scheduled_event_id = 1;
Expand All @@ -91,6 +92,17 @@ message NexusHandlerFailureInfo {
temporal.api.enums.v1.NexusHandlerErrorRetryBehavior retry_behavior = 2;
}

// Representation of the Nexus SDK OperationError object.
message NexusSDKOperationFailureInfo {
string state = 1;
Copy link
Member

Choose a reason for hiding this comment

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

What is "state" here? May need just a quick comment about what's accepted or what it refers to.

Copy link
Member Author

Choose a reason for hiding this comment

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

It's failed | canceled per the Nexus spec. We leave it as a string to allow adding statuses on the Nexus side without requiring a Temporal upgrade.

Copy link
Member

Choose a reason for hiding this comment

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

👍 Worth adding a comment here saying this is an enum defined in the Nexus spec

}

// Representation of the Nexus SDK FailureError object.
message NexusSDKFailureErrorFailureInfo {
map<string, string> metadata = 1;
bytes data = 2;
}

message Failure {
string message = 1;
// The source this Failure originated in, e.g. TypeScriptSDK / JavaSDK
Expand Down Expand Up @@ -125,6 +137,8 @@ message Failure {
ChildWorkflowExecutionFailureInfo child_workflow_execution_failure_info = 12;
NexusOperationFailureInfo nexus_operation_execution_failure_info = 13;
NexusHandlerFailureInfo nexus_handler_failure_info = 14;
NexusSDKOperationFailureInfo nexus_sdk_operation_failure_info = 15;
Copy link
Contributor

Choose a reason for hiding this comment

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

I am worried about adding this, can we confirm this error won't end up as the cause of a nexus operation failure in history?

Copy link
Member Author

Choose a reason for hiding this comment

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

Yeah, the idea is that it would only be used in non-workflow callers and in handlers. But it may still show up in the cause chain in workflow, just not the top level failure.

Copy link
Contributor

Choose a reason for hiding this comment

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

When would this be part of the cause chain in a workflow?

Copy link
Member Author

Choose a reason for hiding this comment

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

Since any failure can appear as a cause, there's nothing preventing users from setting OperationError as the cause of an ApplicationError or HandlerError. I don't expect this to typically happen though.
Maybe when we have non-workflow callers and your operation handler uses the client to forward the operation and the handler wraps that error. Here's an example:

func (*myHandler) StartOperation(...) (...) {
  sc := temporalnexus.GetClient(...).NexusServiceClient(...)
  _, err := sc.StartOperation(...)
  return fmt.Errorf("failed to forward call: %w", err)
}

Copy link
Contributor

Choose a reason for hiding this comment

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

Yes that's true, my main concern is we don't change the current error chains type users see in workflows. Can we confirm that will remain the same?

Copy link
Member Author

Choose a reason for hiding this comment

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

Yup, those will stay the same except for that we won't construct an implicit ApplicationError as the cause when users call construct a handler error with a message and both caller and handler have been upgraded.

NexusSDKFailureErrorFailureInfo nexus_sdk_failure_error_info = 16;
}
}

Expand Down
12 changes: 12 additions & 0 deletions temporal/api/nexus/v1/message.proto
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ option csharp_namespace = "Temporalio.Api.Nexus.V1";
import "google/protobuf/timestamp.proto";
import "temporal/api/common/v1/message.proto";
import "temporal/api/enums/v1/nexus.proto";
import "temporal/api/failure/v1/message.proto";

// A general purpose failure message.
// See: https://github.com/nexus-rpc/api/blob/main/SPEC.md#failure
Expand Down Expand Up @@ -77,6 +78,12 @@ message CancelOperationRequest {

// A Nexus request.
message Request {
message Capabilities {
// If set, handlers may use temporal.api.failure.v1.Failure instances to return failures to the server.
// This also allows handler and operation errors to have their own messages and stack traces.
bool temporal_failure_responses = 1;
}

// Headers extracted from the original request in the Temporal frontend.
// When using Nexus over HTTP, this includes the request's HTTP headers ignoring multiple values.
map<string, string> header = 1;
Expand All @@ -86,6 +93,8 @@ message Request {
// aip.dev/not-precedent: Not following linter rules. --)
google.protobuf.Timestamp scheduled_time = 2;

Capabilities capabilities = 100;
Copy link
Member

Choose a reason for hiding this comment

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

Who is responsible for setting this? I assume the server correct? So is this Nexus Request message only expected to ever be constructed by the server? If this is a Temporal-server-set-only thing, should it instead be on PollNexusTaskQueueResponse?

Copy link
Member Author

Choose a reason for hiding this comment

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

Either of these are constructed only by the server. No strong opinion on where this should go.

Copy link
Member

Choose a reason for hiding this comment

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

I like the idea of being on the poll response myself, but I similarly do not have a strong opinion


oneof variant {
StartOperationRequest start_operation = 3;
CancelOperationRequest cancel_operation = 4;
Expand Down Expand Up @@ -114,6 +123,9 @@ message StartOperationResponse {
Async async_success = 2;
// The operation completed unsuccessfully (failed or canceled).
UnsuccessfulOperationError operation_error = 3;
// The operation completed unsuccessfully (failed or canceled).
// Failure object must contain a NexusSDKOperationFailureInfo object.
temporal.api.failure.v1.Failure failure = 4;
Copy link
Member

Choose a reason for hiding this comment

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

May want to say if this is set, the operation_error is ignored, but not that important

Copy link
Member Author

Choose a reason for hiding this comment

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

It's already a oneof so that's not possible.

}
}

Expand Down
6 changes: 4 additions & 2 deletions temporal/api/workflowservice/v1/request_response.proto
Original file line number Diff line number Diff line change
Expand Up @@ -1834,8 +1834,10 @@ message RespondNexusTaskFailedRequest {
string identity = 2;
// A unique identifier for this task.
bytes task_token = 3;
// The error the handler failed with.
// The error the handler failed with (DEPRECATED, use the failure field instead).
temporal.api.nexus.v1.HandlerError error = 4;
// The error the handler failed with. Must contain a NexusHandlerFailureInfo object.
temporal.api.failure.v1.Failure failure = 5;
}

message RespondNexusTaskFailedResponse {
Expand Down Expand Up @@ -2591,4 +2593,4 @@ message DescribeWorkerRequest {

message DescribeWorkerResponse {
temporal.api.worker.v1.WorkerInfo worker_info = 1;
}
}
Loading