hibiken/asynq · error

asynq

Error message

asynq: %w

What it means

PeriodicTaskManager.Start wraps any error from the initial configuration sync with the "asynq: " prefix. The underlying cause is almost always the config provider failing (GetConfigs error) or a config failing validation. It is a wrapper, so inspect the wrapped error for the real cause.

Solutions

  1. Read the wrapped error (%w chain) to find the root cause
  2. Verify the ConfigProvider is initialized and its backing store is reachable before Start
  3. Validate provider output (non-nil, valid cronspecs) before returning it from GetConfigs
Defensive patterns

Strategy: try-catch

Validate before calling

if err := mgr.p.GetConfigs(); err != nil {
    return fmt.Errorf("provider not ready: %w", err)
}

Try / catch

if err := mgr.Start(); err != nil {
    var root error
    for e := err; e != nil; e = errors.Unwrap(e) { root = e }
    log.Fatalf("periodic task manager failed to start: %v (root: %v)", err, root)
}

Prevention

When it happens

Trigger: Calling mgr.Start() (directly or via Run) when mgr.p.GetConfigs() returns an error, or when initialSync validation fails (nil config, nil Task, empty Cronspec, duplicate IDs).

Common situations: Redis-backed or file-based provider unreachable at startup; provider returns configs with empty cronspecs; app redeploy where the config service is not yet up.

Understand the failure class

Background: "Invalid value" and "allowed values are" config errors: what your library rejected and how to fix it — this error's family across 41 libraries.

Related errors


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

Appendix: source

Thrown at periodic_task_manager.go:125

	if c.Task == nil {
		return fmt.Errorf("PeriodicTaskConfig.Task cannot be nil")
	}
	if c.Cronspec == "" {
		return fmt.Errorf("PeriodicTaskConfig.Cronspec cannot be empty")
	}
	return nil
}

// Start starts a scheduler and background goroutine to sync the scheduler with the configs
// returned by the provider.
//
// Start returns any error encountered at start up time.
func (mgr *PeriodicTaskManager) Start() error {
	if mgr.s == nil || mgr.p == nil {
		panic("asynq: cannot start uninitialized PeriodicTaskManager; use NewPeriodicTaskManager to initialize")
	}
	if err := mgr.initialSync(); err != nil {
		return fmt.Errorf("asynq: %w", err)
	}
	if err := mgr.s.Start(); err != nil {
		return fmt.Errorf("asynq: %w", err)
	}
	mgr.wg.Add(1)
	go func() {
		defer mgr.wg.Done()
		ticker := time.NewTicker(mgr.syncInterval)
		for {
			select {
			case <-mgr.done:
				mgr.s.logger.Debugf("Stopping syncer goroutine")
				ticker.Stop()
				return
			case <-ticker.C:
				mgr.sync()
			}
		}

View on GitHub (pinned to d135f1439b)