temporalio/temporal · error · NotFound (wrapped ErrStaleReference)
%w: zombie workflow cannot be updated
Error message
%w: zombie workflow cannot be updated
What it means
ErrWorkflowZombie wraps ErrStaleReference and indicates a workflow execution is in the zombie state — the workflow already completed but its mutable state still exists, so it can no longer accept updates. History service returns it from CHASM-related validation paths (validateNotZombieWorkflow, validateChasmSideEffectTask, executeChasmPureTimers).
Source
Thrown at service/history/consts/const.go:53
// task is no longer needed.
// It is also a NotFoundError to indicate to API callers that the object they're targeting is not found.
ErrStaleReference = serviceerror.NewNotFound("stale reference")
// ErrStaleState is the error returned during state update indicating that cached mutable state could be stale after
// a reload attempt.
ErrStaleState = staleStateError{}
// ErrTransitionHistoryDisabled is the error to indicate that transition history is disabled for the state machine,
// and request cannot be processed before it's re-enabled.
ErrTransitionHistoryDisabled = serviceerror.NewFailedPrecondition("Transition history disabled")
// ErrActivityTaskNotFound is the error to indicate activity task could be duplicate and activity already completed
ErrActivityTaskNotFound = serviceerror.NewNotFound("invalid activityID or activity already timed out or invoking workflow is completed")
// ErrActivityNotFound is the error to indicate that there is no pending activity with this ID
ErrActivityNotFound = serviceerror.NewNotFound("Can't find pending activity with such ID. Invalid activityID or activity already completed")
// ErrActivityTaskNotCancelRequested is the error to indicate activity to be canceled is not cancel requested
ErrActivityTaskNotCancelRequested = serviceerror.NewInvalidArgument("unable to mark activity as canceled without activity being request canceled first")
// ErrWorkflowCompleted is the error to indicate workflow execution already completed
ErrWorkflowCompleted = serviceerror.NewNotFound("workflow execution already completed")
// ErrWorkflowZombie is the error to indicate workflow execution is in zombie state and cannot be updated
ErrWorkflowZombie = fmt.Errorf("%w: zombie workflow cannot be updated", ErrStaleReference)
// ErrWorkflowExecutionNotFound is the error to indicate workflow execution does not exist
ErrWorkflowExecutionNotFound = serviceerror.NewNotFound("workflow execution not found")
// ErrWorkflowParent is the error to parent execution is given and mismatch
ErrWorkflowParent = serviceerror.NewNotFound("workflow parent does not match")
// ErrDeserializingToken is the error to indicate task token is invalid
ErrDeserializingToken = serviceerror.NewInvalidArgument("error deserializing task token")
// ErrSignalsLimitExceeded is the error indicating limit reached for maximum number of signal events
ErrSignalsLimitExceeded = serviceerror.NewInvalidArgument("exceeded workflow execution limit for signal events")
// ErrWorkflowClosing is the error indicating requests to workflow can not be applied as workflow is closing
ErrWorkflowClosing = &serviceerror.ResourceExhausted{
Cause: enumspb.RESOURCE_EXHAUSTED_CAUSE_BUSY_WORKFLOW,
Scope: enumspb.RESOURCE_EXHAUSTED_SCOPE_NAMESPACE,
Message: "workflow operation can not be applied because workflow is closing",
}
// ErrEventsAterWorkflowFinish is the error indicating server error trying to write events after workflow finish event
ErrEventsAterWorkflowFinish = serviceerror.NewInternal("error validating last event being workflow finish event")
// ErrQueryEnteredInvalidState is error indicating query entered invalid state
ErrQueryEnteredInvalidState = serviceerror.NewInvalidArgument("query entered invalid state, this should be impossible")View on GitHub (pinned to bde624efd1)
Solutions
- Treat as stale reference: discard the task/update rather than retrying — the workflow is done
- Check for replication lag or duplicate task delivery if this happens frequently for the same workflow
- If the workflow should not be complete, investigate why it finished (timeout, termination) before resending updates
Example fix
// before
err := workflowContext.Update(...)
// after
err := workflowContext.Update(...)
if errors.Is(err, consts.ErrWorkflowZombie) {
// workflow already completed; drop the update
return nil
} Defensive patterns
Strategy: type-guard
Type guard
func isZombieWorkflow(err error) bool { return errors.Is(err, consts.ErrWorkflowZombie) } Try / catch
if err := updateWorkflow(ctx, wf); err != nil {
if errors.Is(err, consts.ErrWorkflowZombie) {
// workflow already completed; drop the update idempotently
return nil
}
return err
} Prevention
- Check workflow completion status before issuing updates/tasks
- Design task handlers to tolerate workflow-completed races idempotently
- Monitor replication lag which widens the zombie window
When it happens
Trigger: Any attempt to update a workflow whose mutable state shows zombie status: validating a CHASM side-effect task, executing CHASM pure timers or state-machine timers against a workflow that completed before the task ran.
Common situations: A workflow completed (timeout, cancellation, termination) while completion/replication tasks were still in flight; replication lag lets one cluster finish the workflow while another still processes tasks for it; retry of an already-applied task after workflow completion.
Related errors
- ErrStaleReference
- ErrInvalidTransition
- failed to deserialize component: %w
- failed to deserialize data: %w
- %w: ref transition count out of range for version %v: %v not
AI-assisted analysis of temporalio/temporal@bde624efd1 (2026-09-01).
Data as JSON: /api/errors/c44db75ed28d3272.
Report an issue: GitHub.