hibiken/asynq · error

initial call to GetConfigs contained an invalid config

Error message

initial call to GetConfigs contained an invalid config: %w

What it means

initialSync validates every config returned by GetConfigs; this error means one of them failed validation (nil config, nil Task, empty Cronspec, or duplicate EntryID). The whole sync aborts rather than partially registering configs, so the manager fails to start.

Solutions

  1. Log/iterate provider configs locally and run the same validation to find the offending entry
  2. Fix the invalid entry (set Task and Cronspec, assign a unique EntryID)
  3. Make the provider skip or sanitize invalid entries before returning them

Example fix

// before
configs := append(cfgs, &PeriodicTaskConfig{}) // zero value
// after
configs := append(cfgs, NewPeriodicTaskConfig(task, "*/10 * * * *"))
Defensive patterns

Strategy: validation

Validate before calling

for _, c := range configs {
    if c == nil || c.Task == nil || c.Cronspec == "" {
        return errors.New("provider returned an invalid periodic task config")
    }
}
ids := map[string]bool{}
for _, c := range configs {
    if ids[c.EntryID] { return fmt.Errorf("duplicate EntryID %q", c.EntryID) }
    ids[c.EntryID] = true
}

Prevention

When it happens

Trigger: Provider's GetConfigs returned a list where at least one PeriodicTaskConfig is invalid: config==nil, config.Task==nil, Cronspec=="", or a repeated EntryID.

Common situations: A newly added entry in the config store has a typo'd/empty cron field; two entries accidentally share an ID after copy-paste; provider code appends a zero-value config on error paths.

Understand the failure class

Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.

Related errors


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

Appendix: source

Thrown at periodic_task_manager.go:175

// Once it receives a signal, it gracefully shuts down the manager.
func (mgr *PeriodicTaskManager) Run() error {
	if err := mgr.Start(); err != nil {
		return err
	}
	mgr.s.waitForSignals()
	mgr.Shutdown()
	mgr.s.logger.Debugf("PeriodicTaskManager exiting")
	return nil
}

func (mgr *PeriodicTaskManager) initialSync() error {
	configs, err := mgr.p.GetConfigs()
	if err != nil {
		return fmt.Errorf("initial call to GetConfigs failed: %w", err)
	}
	for _, c := range configs {
		if err := validatePeriodicTaskConfig(c); err != nil {
			return fmt.Errorf("initial call to GetConfigs contained an invalid config: %w", err)
		}
	}
	mgr.add(configs)
	return nil
}

func (mgr *PeriodicTaskManager) add(configs []*PeriodicTaskConfig) {
	for _, c := range configs {
		entryID, err := mgr.s.Register(c.Cronspec, c.Task, c.Opts...)
		if err != nil {
			mgr.s.logger.Errorf("Failed to register periodic task: cronspec=%q task=%q err=%v",
				c.Cronspec, c.Task.Type(), err)
			continue
		}
		mgr.m[c.hash()] = entryID
		mgr.s.logger.Infof("Successfully registered periodic task: cronspec=%q task=%q, entryID=%s",
			c.Cronspec, c.Task.Type(), entryID)
	}

View on GitHub (pinned to d135f1439b)