{"record":{"id":"a4ee6b37e6b5d09b","repo":"temporalio/temporal","slug":"errcancellationalreadyrequested","errorCode":"ErrCancellationAlreadyRequested","errorMessage":"%w with request ID %s","messagePattern":"%w with request ID (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"chasm/lib/nexusoperation/operation.go","lineNumber":190,"sourceCode":"// operation has already started, schedules the cancellation request to be sent to the Nexus endpoint.\nfunc (o *Operation) RequestCancel(\n\tctx chasm.MutableContext,\n\treq *nexusoperationpb.CancellationState,\n) error {\n\t// A cancel retry can arrive after the operation closed, so dedupe before rejecting terminal states.\n\texistingCancellation, hasCanceled := o.Cancellation.TryGet(ctx)\n\tif hasCanceled &&\n\t\texistingCancellation.GetRequestId() == req.GetRequestId() {\n\t\treturn nil\n\t}\n\n\tif !TransitionCanceled.Possible(o) {\n\t\treturn ErrOperationAlreadyCompleted\n\t}\n\n\tif hasCanceled {\n\t\texistingReqID := existingCancellation.GetRequestId()\n\t\treturn fmt.Errorf(\"%w with request ID %s\", ErrCancellationAlreadyRequested, existingReqID)\n\t}\n\n\tcancel := newCancellation(req)\n\to.Cancellation = chasm.NewComponentField(ctx, cancel)\n\t// Once started, the handler returns a token that can be used in the cancellation request.\n\t// Until then, no need to schedule the cancellation.\n\tif o.Status == nexusoperationpb.OPERATION_STATUS_STARTED {\n\t\treturn TransitionCancellationScheduled.Apply(cancel, ctx, EventCancellationScheduled{\n\t\t\tDestination: o.GetEndpoint(),\n\t\t})\n\t}\n\treturn nil\n}\n\n// onStarted applies the started transition or delegates to the store if one is present.\nfunc (o *Operation) onStarted(ctx chasm.MutableContext, operationToken string, startTime *time.Time, links []*commonpb.Link) error {\n\tif store, ok := o.Store.TryGet(ctx); ok {\n\t\treturn store.OnNexusOperationStarted(ctx, o, operationToken, startTime, links)","sourceCodeStart":172,"sourceCodeEnd":208,"githubUrl":"https://github.com/temporalio/temporal/blob/bde624efd13fbd3843654058db6d9c716166318b/chasm/lib/nexusoperation/operation.go#L172-L208","documentation":"Requesting cancellation of a Nexus operation fails when a cancellation was already requested. The error wraps ErrCancellationAlreadyRequested and includes the request ID of the existing cancellation request so callers can deduplicate or correlate their requests. It is returned when the operation's Cancellation component already exists.","triggerScenarios":"Calling chasm NexusOperation RequestCancel (via ClientWorkflow NexusOperationCancel or the CancelNexusOperation API) on an operation whose Cancellation field is already set, i.e. cancel was previously requested (possibly by a different workflow run or a retry).","commonSituations":"A workflow retried a cancellation call after an ambiguous failure (timeout with unknown outcome); two workflows or two calls try to cancel the same operation; an operator manually re-issues a cancel that automation already sent.","solutions":["Treat this as expected on retries: check errors.Is(err, ErrCancellationAlreadyRequested) and treat cancellation as in-flight, not failed","Use the request ID in the message to compare with your own request ID — if they match, the earlier cancel succeeded","Do not retry the cancellation with a new request ID; a fresh request ID will not be honored for an existing cancellation","If the operation actually completed despite this, check operation status first (TransitionCanceled.Possible) before cancelling"],"exampleFix":"// before\nerr := nexusOp.RequestCancel(ctx, req)\nif err != nil { return err }\n// after\nerr := nexusOp.RequestCancel(ctx, req)\nif errors.Is(err, ErrCancellationAlreadyRequested) {\n    // cancellation already in flight; proceed waiting for result\n    return nil\n}\nif err != nil { return err }","handlingStrategy":"try-catch","validationCode":"// Before cancelling, check operation state where accessible:\nif !TransitionCanceled.Possible(op) { /* already completed; skip cancel */ }","typeGuard":null,"tryCatchPattern":"// Go\nerr := nexusOp.RequestCancel(ctx, req)\nswitch {\ncase err == nil:\n    // cancellation newly requested\ncase errors.Is(err, ErrCancellationAlreadyRequested):\n    // idempotent path: cancellation already in flight — not a failure\ndefault:\n    return err\n}","preventionTips":["Reuse the same request ID when retrying cancel operations so duplicates are detectable","Track cancellation state in your workflow to avoid duplicate cancel calls","Check operation completion status before issuing a cancel","Treat ErrCancellationAlreadyRequested as success in idempotent workflows"],"tags":["nexus","nexusoperation","cancellation","idempotency"],"backgroundTag":"cancellation-already-requested","analyzedSha":"bde624efd13fbd3843654058db6d9c716166318b","analyzedAt":"2026-09-01T07:18:39.080Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}