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
- Pass a populated map[string]TaskExecutor (history, sync activity, sync workflow state, sync HSM executors) to newDLQHandler
- Check the bootstrap/DI code path that builds task executors before NewLazyDLQHandler is called
- 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
- Wire all replication task executors in bootstrap before NewLazyDLQHandler
- In tests, build a real or mocked executor map instead of nil
- Add a startup-time check that the executor map covers all expected replication task types
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
- Unknown repication task type: %v
- queue already exists
- BatchSize too large
- %w: state namespace failover version < ref namespace failove
- %w: state namespace failover version > ref namespace failove
AI-assisted analysis of temporalio/temporal@bde624efd1 (2026-09-01).
Data as JSON: /api/errors/71c845a448be2c06.
Report an issue: GitHub.