temporalio/temporal · error

Failed to initialize replication DLQ handler due to nil task

Error message

Failed to initialize replication DLQ handler due to nil task executors

What it means

newDLQHandler builds the replication DLQ (dead-letter queue) handler and requires a non-nil map of task executors to process fetched replication tasks. A nil map means the handler could never execute any DLQ task, so it panics immediately at construction. This is a wiring/dependency-injection invariant, not a runtime condition.

Source

Thrown at service/history/replication/dlq_handler.go:87

		deleteManager,
		workflowCache,
		clientBean,
		make(map[string]TaskExecutor),
		taskExecutorProvider,
	)
}

func newDLQHandler(
	shard historyi.ShardContext,
	deleteManager deletemanager.DeleteManager,
	workflowCache wcache.Cache,
	clientBean client.Bean,
	taskExecutors map[string]TaskExecutor,
	taskExecutorProvider TaskExecutorProvider,
) *dlqHandlerImpl {

	if taskExecutors == nil {
		panic("Failed to initialize replication DLQ handler due to nil task executors")
	}
	return &dlqHandlerImpl{
		shard:         shard,
		deleteManager: deleteManager,
		workflowCache: workflowCache,
		remoteHistoryFetcher: eventhandler.NewHistoryPaginatedFetcher(
			shard.GetNamespaceRegistry(),
			clientBean,
			shard.GetPayloadSerializer(),
			shard.GetLogger(),
		),
		taskExecutors:        taskExecutors,
		taskExecutorProvider: taskExecutorProvider,
		logger:               shard.GetLogger(),
	}
}

func (r *dlqHandlerImpl) GetMessages(

View on GitHub (pinned to bde624efd1)

Solutions

  1. Pass a populated map[string]TaskExecutor (history, sync activity, sync workflow state, sync HSM executors) to newDLQHandler
  2. Check the bootstrap/DI code path that builds task executors before NewLazyDLQHandler is called
  3. In tests, construct the real executor map or mock TaskExecutor entries instead of passing nil

Example fix

// before
handler := NewLazyDLQHandler(shard, deleteManager, cache, clientBean, nil, provider)
// after
executors := map[string]TaskExecutor{
  enumsspb.TASK_TYPE_REPLICATION_HISTORY.String(): historyExecutor,
  enumsspb.TASK_TYPE_REPLICATION_SYNC_ACTIVITY.String(): activityExecutor,
}
handler := NewLazyDLQHandler(shard, deleteManager, cache, clientBean, executors, provider)
Defensive patterns

Strategy: validation

Validate before calling

// Before constructing the handler:
if taskExecutors == nil || len(taskExecutors) == 0 {
  return fmt.Errorf("task executors map must be populated before building DLQ handler")
}

Prevention

When it happens

Trigger: Calling newDLQHandler directly with a nil taskExecutors map; a DI/constructor change (e.g. NewLazyDLQHandler callers) that stopped populating task executors; test setup (SetupTest) passing nil executors.

Common situations: Refactoring history service bootstrap and forgetting to wire replication task executors; test scaffolding constructing the handler without executors.

Related errors


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