temporalio/temporal · warning

ErrCancellationAlreadyRequested

ErrCancellationAlreadyRequested

Error message

%w with request ID %s

What it means

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.

Source

Thrown at chasm/lib/nexusoperation/operation.go:190

// operation has already started, schedules the cancellation request to be sent to the Nexus endpoint.
func (o *Operation) RequestCancel(
	ctx chasm.MutableContext,
	req *nexusoperationpb.CancellationState,
) error {
	// A cancel retry can arrive after the operation closed, so dedupe before rejecting terminal states.
	existingCancellation, hasCanceled := o.Cancellation.TryGet(ctx)
	if hasCanceled &&
		existingCancellation.GetRequestId() == req.GetRequestId() {
		return nil
	}

	if !TransitionCanceled.Possible(o) {
		return ErrOperationAlreadyCompleted
	}

	if hasCanceled {
		existingReqID := existingCancellation.GetRequestId()
		return fmt.Errorf("%w with request ID %s", ErrCancellationAlreadyRequested, existingReqID)
	}

	cancel := newCancellation(req)
	o.Cancellation = chasm.NewComponentField(ctx, cancel)
	// Once started, the handler returns a token that can be used in the cancellation request.
	// Until then, no need to schedule the cancellation.
	if o.Status == nexusoperationpb.OPERATION_STATUS_STARTED {
		return TransitionCancellationScheduled.Apply(cancel, ctx, EventCancellationScheduled{
			Destination: o.GetEndpoint(),
		})
	}
	return nil
}

// onStarted applies the started transition or delegates to the store if one is present.
func (o *Operation) onStarted(ctx chasm.MutableContext, operationToken string, startTime *time.Time, links []*commonpb.Link) error {
	if store, ok := o.Store.TryGet(ctx); ok {
		return store.OnNexusOperationStarted(ctx, o, operationToken, startTime, links)

View on GitHub (pinned to bde624efd1)

Solutions

  1. Treat this as expected on retries: check errors.Is(err, ErrCancellationAlreadyRequested) and treat cancellation as in-flight, not failed
  2. Use the request ID in the message to compare with your own request ID — if they match, the earlier cancel succeeded
  3. Do not retry the cancellation with a new request ID; a fresh request ID will not be honored for an existing cancellation
  4. If the operation actually completed despite this, check operation status first (TransitionCanceled.Possible) before cancelling

Example fix

// before
err := nexusOp.RequestCancel(ctx, req)
if err != nil { return err }
// after
err := nexusOp.RequestCancel(ctx, req)
if errors.Is(err, ErrCancellationAlreadyRequested) {
    // cancellation already in flight; proceed waiting for result
    return nil
}
if err != nil { return err }
Defensive patterns

Strategy: try-catch

Validate before calling

// Before cancelling, check operation state where accessible:
if !TransitionCanceled.Possible(op) { /* already completed; skip cancel */ }

Try / catch

// Go
err := nexusOp.RequestCancel(ctx, req)
switch {
case err == nil:
    // cancellation newly requested
case errors.Is(err, ErrCancellationAlreadyRequested):
    // idempotent path: cancellation already in flight — not a failure
default:
    return err
}

Prevention

When it happens

Trigger: 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).

Common situations: 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.

Related errors


AI-assisted analysis of temporalio/temporal@bde624efd1 (2026-09-01). Data as JSON: /api/errors/a4ee6b37e6b5d09b. Report an issue: GitHub.