hibiken/asynq · error

PeriodicTaskConfig.Cronspec cannot be empty

Error message

PeriodicTaskConfig.Cronspec cannot be empty

What it means

validatePeriodicTaskConfig checks each PeriodicTaskConfig supplied by the config provider. This error means a config had a non-nil Task but an empty Cronspec string, which asynq cannot schedule since the cron expression defines when the task runs. The manager refuses to register such a config, aborting the initial sync or periodic sync.

Solutions

  1. Set a valid cron expression on the config, e.g. NewPeriodicTaskConfig(task, "*/5 * * * *")
  2. Fix the provider/config source so the cronspec field is populated (check env var / YAML key / DB column)
  3. Add startup validation on your provider side to fail fast on empty specs before asynq sees them

Example fix

// before
&PeriodicTaskConfig{Task: cleanupTask}
// after
&PeriodicTaskConfig{Task: cleanupTask, Cronspec: "0 2 * * *"}
Defensive patterns

Strategy: validation

Validate before calling

for i, c := range configs {
    if c == nil || c.Task == nil || c.Cronspec == "" {
        return fmt.Errorf("config %d invalid: nil=%v taskNil=%v emptySpec=%v", i, c == nil, c != nil && c.Task == nil, c != nil && c.Cronspec == "")
    }
}

Type guard

func validPeriodicConfig(c *asynq.PeriodicTaskConfig) bool {
    return c != nil && c.Task != nil && c.Cronspec != ""
}

Prevention

When it happens

Trigger: A ConfigProvider returned a PeriodicTaskConfig whose Cronspec field is "" (never set, or set from a variable/flag that defaulted to empty). Produced by initialSync at startup or the periodic sync loop in Run.

Common situations: Loading cron specs from environment variables or config files where the key is missing; constructing PeriodicTaskConfig{Task: task} and forgetting NewPeriodicTaskConfig sets the spec; upstream config store returning blank specs.

Understand the failure class

Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.

Related errors


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

Appendix: source

Thrown at periodic_task_manager.go:111

	_, _ = h.Write([]byte(c.Task.Type()))
	h.Write(c.Task.Payload())
	opts := stringifyOptions(c.Opts)
	sort.Strings(opts)
	for _, opt := range opts {
		_, _ = h.Write([]byte(opt))
	}
	return fmt.Sprintf("%x", h.Sum(nil))
}

func validatePeriodicTaskConfig(c *PeriodicTaskConfig) error {
	if c == nil {
		return fmt.Errorf("PeriodicTaskConfig cannot be nil")
	}
	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)
	}

View on GitHub (pinned to d135f1439b)