temporalio/temporal · error

<re-panicked recovered value>

Error message

<re-panicked recovered value>

What it means

This is a re-panic guard inside the CHASM side-effect task execution path (tree.go). A deferred recover() checks for a panic raised while the task (validator/execute) runs; before re-panicking it clears chasmTask.DeserializedTask so the deserialization cache doesn't retain a stale reflect.Value around the panicking call. The visible message is the original recovered value being re-thrown — the panic itself originates from user task code or framework internals, and this frame only preserves it while cleaning up cache state.

Source

Thrown at chasm/tree.go:3651

			}
		}
		if logicalTask == nil {
			return false, false, nil
		}
	}

	// All structural checks passed — the task exists in the tree.

	// Component must be hydrated before the task's validator is called.
	validateCtx := NewContext(NewContextWithOperationIntent(ctx, OperationIntentProgress), n)
	if err := node.prepareComponentValue(validateCtx); err != nil {
		return false, false, err
	}

	defer func() {
		if rec := recover(); rec != nil {
			chasmTask.DeserializedTask = reflect.Value{}
			panic(rec) //nolint:forbidigo
		}
		if retErr != nil {
			chasmTask.DeserializedTask = reflect.Value{}
		}
	}()

	if !chasmTask.DeserializedTask.IsValid() {
		var err error
		if logicalTask != nil {
			// Use the logical task's Data pointer so deserialization shares the
			// node's taskValueCache with closeTransactionCleanupInvalidTasks.
			// The physical task's taskInfo.Data is a different pointer (freshly
			// allocated from the physical task row) and would always miss the cache.
			chasmTask.DeserializedTask, err = node.deserializeTaskWithCache(registrableTask, logicalTask.Data)
		} else {
			// Backward compatibility: physical task predates TaskVersionedTransition.
			chasmTask.DeserializedTask, err = deserializeTask(registrableTask, taskInfo.Data)
		}

View on GitHub (pinned to bde624efd1)

Solutions

  1. Look below this frame in the stack trace for the original panic in task Validate/Execute code and fix that root cause
  2. Add defensive checks in the task implementation for nil values and failed type assertions before using deserialized data
  3. Verify the component's data payloads in persistence are compatible with the current struct definitions (schema drift can cause invalid values)
  4. If the panic is inside framework code, report with the full stack trace

Example fix

// before
func (t *MyTask) Execute(ctx chasm.Context, c *Component) (Result, error) {
  return c.Order.Status.Value, nil // panics if Order is nil
}
// after
func (t *MyTask) Execute(ctx chasm.Context, c *Component) (Result, error) {
  if c == nil || c.Order == nil {
    return Result{}, errors.New("order not initialized")
  }
  return c.Order.Status.Value, nil
}
Defensive patterns

Strategy: try-catch

Validate before calling

// defensively validate deserialized inputs inside task code before use
if task == nil || !task.Payload.IsValid() {
    return errors.New("invalid task payload")
}

Try / catch

defer func() {
    if r := recover(); r != nil {
        logger.Error("panic in chasm task", "recover", r, "stack", debug.Stack())
        // convert to error return where the task API allows
    }
}()

Prevention

When it happens

Trigger: Any panic inside the side-effect task's Validate or Execute invocation (after hydration, within the deferred-recover scope) — e.g. nil map write, index out of range, or explicit panic in user-registered CHASM task code.

Common situations: User CHASM task implementations panicking on unexpected input (nil derefs, failed type assertions); framework deserialization producing an invalid value that user code then misuses; panics in validators during task rehydration from persistence.

Related errors


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