hibiken/asynq · error

PeriodicTaskConfigProvider cannot be nil

Error message

PeriodicTaskConfigProvider cannot be nil

What it means

NewPeriodicTaskManager validates its options before constructing the manager and returns this error when PeriodicTaskConfigProvider is nil. The provider is the callback that supplies the list of periodic task configs during sync, so the manager cannot function without it. This fails fast at construction time.

Solutions

  1. Set PeriodicTaskConfigProvider to a function returning ([]*PeriodicTaskConfig, error)
  2. Double-check the PeriodicTaskManagerOpts literal includes the provider
  3. Add a unit test constructing the manager with production opts

Example fix

// before
mgr, err := asynq.NewPeriodicTaskManager(asynq.PeriodicTaskManagerOpts{
    RedisConnOpt: asynq.RedisClientOpt{Addr: redisAddr},
})
// after
mgr, err := asynq.NewPeriodicTaskManager(asynq.PeriodicTaskManagerOpts{
    RedisConnOpt: asynq.RedisClientOpt{Addr: redisAddr},
    PeriodicTaskConfigProvider: getPeriodicConfigs,
})
Defensive patterns

Strategy: validation

Validate before calling

if opts.PeriodicTaskConfigProvider == nil {
    return errors.New("PeriodicTaskConfigProvider must be set")
}
mgr, err := asynq.NewPeriodicTaskManager(opts)

Try / catch

mgr, err := asynq.NewPeriodicTaskManager(opts)
if err != nil {
    log.Fatalf("invalid periodic task manager opts: %v", err)
}

Prevention

When it happens

Trigger: Calling NewPeriodicTaskManager(PeriodicTaskManagerOpts{}) or omitting the PeriodicTaskConfigProvider field while setting RedisConnOpt/SchedulerOpts.

Common situations: Copy-pasting an example and dropping the provider field; building opts dynamically where the provider assignment is skipped; refactoring that renamed the field.

Understand the failure class

Background: "Must pass :limit option" / "Missing required option" — required option errors explained — this error's family across 41 libraries.

Related errors


AI-assisted analysis of hibiken/asynq@d135f1439b (2026-09-07). Data as JSON: /api/errors/dd7d6ed55f0fcaf4. Report an issue: GitHub.

Appendix: source

Thrown at periodic_task_manager.go:51

	RedisConnOpt RedisConnOpt

	// Optional: if RedisUniversalClient is non nil, RedisConnOpt is ignored.
	RedisUniversalClient redis.UniversalClient

	// Optional: scheduler options
	*SchedulerOpts

	// Optional: default is 3m
	SyncInterval time.Duration
}

const defaultSyncInterval = 3 * time.Minute

// NewPeriodicTaskManager returns a new PeriodicTaskManager instance.
// The given opts should specify the RedisConnOp and PeriodicTaskConfigProvider at minimum.
func NewPeriodicTaskManager(opts PeriodicTaskManagerOpts) (*PeriodicTaskManager, error) {
	if opts.PeriodicTaskConfigProvider == nil {
		return nil, fmt.Errorf("PeriodicTaskConfigProvider cannot be nil")
	}
	if opts.RedisConnOpt == nil && opts.RedisUniversalClient == nil {
		return nil, fmt.Errorf("RedisConnOpt/RedisUniversalClient cannot be nil")
	}
	var scheduler *Scheduler
	if opts.RedisUniversalClient != nil {
		scheduler = NewSchedulerFromRedisClient(opts.RedisUniversalClient, opts.SchedulerOpts)
	} else {
		scheduler = NewScheduler(opts.RedisConnOpt, opts.SchedulerOpts)
	}

	syncInterval := opts.SyncInterval
	if syncInterval == 0 {
		syncInterval = defaultSyncInterval
	}
	return &PeriodicTaskManager{
		s:            scheduler,
		p:            opts.PeriodicTaskConfigProvider,

View on GitHub (pinned to d135f1439b)