temporalio/temporal · error

panic(rec)

Error message

panic(rec)

What it means

In NDC WorkflowStateReplicator.Apply, a deferred recover() handler releases the versioned-transition artifact lock with errPanic semantics and then re-panics with panic(rec) to preserve the original panic. The re-panic is intentional: the lock must be released with the panic error before the panic propagates upward.

Source

Thrown at service/history/ndc/workflow_state_replicator.go:300

	if err != nil {
		return err
	}
	if emitLifecycle {
		// Snapshot the post-apply mutable state at release time: the only point that is both after
		// the apply (which mutates ms in place) and still under the workflow lock, since the apply
		// releases via the transaction manager.
		innerReleaseFn := releaseFn
		releaseFn = func(err error) {
			if err == nil && appliedMS == nil && ms != nil {
				appliedMS = ms.CloneToProto()
			}
			innerReleaseFn(err)
		}
	}
	defer func() {
		if rec := recover(); rec != nil {
			releaseFn(errPanic)
			panic(rec)
		}
		releaseFn(retError)
	}()
	if versionedTransitionArtifact.IsFirstSync {
		// this is the first replication task for this workflow
		// TODO: Handle reset case to reduce the amount of history events write
		continueProcess, err := r.handleFirstReplicationTask(ctx, archetypeID, wfCtx, versionedTransitionArtifact, sourceClusterName, captureMutableState)
		if err != nil || !continueProcess {
			return err
		}
	}

	ms, err = wfCtx.LoadMutableState(ctx, r.shardContext)
	switch err.(type) {
	case *serviceerror.NotFound:
		return r.applySnapshot(ctx, namespaceID, wid, rid, archetypeID, wfCtx, releaseFn, nil, versionedTransitionArtifact, sourceClusterName, captureMutableState)
	case nil:
		localTransitionHistory := ms.GetExecutionInfo().TransitionHistory

View on GitHub (pinned to bde624efd1)

Solutions

  1. Inspect the panic stack trace above this frame to find the real panicking code and fix that bug.
  2. Add validation/nil checks in the replication path that produced the nil/invalid value.
  3. Reproduce with the offending replication task payload in a unit/integration test.
Defensive patterns

Strategy: try-catch

Try / catch

defer func() {
	if rec := recover(); rec != nil {
		logger.Error("replication panic", "stack", string(debug.Stack()))
		panic(rec)
	}
}()

Prevention

When it happens

Trigger: Any code inside Apply's critical section panics (nil pointer on mutable state, invariant violation, index-out-of-range), triggering the deferred recover-and-repanic block.

Common situations: Corrupt or partially-loaded mutable state during replication; nil dereference on missing workflow context; bugs in replication task handling surfacing as panics.

Related errors


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