hibiken/asynq · error

GroupGracePeriod cannot be less than a second

Error message

GroupGracePeriod cannot be less than a second

What it means

This is a panic thrown at server startup when the asynq Config's GroupGracePeriod is set to a positive but sub-second duration. The library intentionally enforces a minimum of one second because grace periods shorter than that would make group aggregation semantics meaningless. Note that a zero value is tolerated and silently falls back to defaultGroupGracePeriod.

Solutions

  1. Set cfg.GroupGracePeriod to at least 1*time.Second (e.g. 10*time.Second).
  2. If you want the default, leave GroupGracePeriod as 0 (zero value) instead of a small duration.
  3. Validate configuration before constructing the server: reject sub-second values with a clear error message.
  4. If the value comes from an env var or file, ensure the unit conversion is correct (e.g. strconv.Atoi then time.Duration(ms)*time.Millisecond).

Example fix

// before
cfg := asynq.Config{ GroupGracePeriod: 500 * time.Millisecond }
srv := asynq.NewServer(redisOpt, cfg)
// after
cfg := asynq.Config{ GroupGracePeriod: 10 * time.Second }
srv := asynq.NewServer(redisOpt, cfg)
Defensive patterns

Strategy: validation

Validate before calling

func validateGracePeriod(d time.Duration) error {
    if d != 0 && d < time.Second {
        return fmt.Errorf("GroupGracePeriod must be >= 1s (or 0 for default), got %s", d)
    }
    return nil
}

Try / catch

// Go panics are not catchable via try/catch; optionally recover in main:
func main() {
    defer func() {
        if r := recover(); r != nil {
            log.Fatalf("invalid config: %v", r)
        }
    }()
    srv := asynq.NewServer(opt, cfg)
    _ = srv
}

Prevention

When it happens

Trigger: Calling asynq.NewServer(redisConnOpt, cfg) with cfg.GroupGracePeriod set to a nonzero duration less than time.Second, e.g. 500*time.Millisecond or 1 time.Nanosecond.

Common situations: Developers converting millisecond-based config values (env vars, YAML) into time.Duration without multiplying by time.Millisecond; assuming any positive duration is valid; confusing GracePeriod with the zero-value fallback behavior.

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/33a9526332f0f153. Report an issue: GitHub.

Appendix: source

Thrown at server.go:497

	var qnames []string
	for q := range queues {
		qnames = append(qnames, q)
	}
	shutdownTimeout := cfg.ShutdownTimeout
	if shutdownTimeout == 0 {
		shutdownTimeout = defaultShutdownTimeout
	}
	healthcheckInterval := cfg.HealthCheckInterval
	if healthcheckInterval == 0 {
		healthcheckInterval = defaultHealthCheckInterval
	}
	// TODO: Create a helper to check for zero value and fall back to default (e.g. getDurationOrDefault())
	groupGracePeriod := cfg.GroupGracePeriod
	if groupGracePeriod == 0 {
		groupGracePeriod = defaultGroupGracePeriod
	}
	if groupGracePeriod < time.Second {
		panic("GroupGracePeriod cannot be less than a second")
	}
	logger := log.NewLogger(cfg.Logger)
	loglevel := cfg.LogLevel
	if loglevel == level_unspecified {
		loglevel = InfoLevel
	}
	logger.SetLevel(toInternalLogLevel(loglevel))

	rdb := rdb.NewRDB(c)
	starting := make(chan *workerInfo)
	finished := make(chan *base.TaskMessage)
	syncCh := make(chan *syncRequest)
	srvState := &serverState{value: srvStateNew}
	cancels := base.NewCancelations()

	syncer := newSyncer(syncerParams{
		logger:     logger,
		requestsCh: syncCh,

View on GitHub (pinned to d135f1439b)