{"record":{"id":"33a9526332f0f153","repo":"hibiken/asynq","slug":"groupgraceperiod-cannot-be-less-than-a-second","errorCode":null,"errorMessage":"GroupGracePeriod cannot be less than a second","messagePattern":"GroupGracePeriod cannot be less than a second","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"server.go","lineNumber":497,"sourceCode":"\tvar qnames []string\n\tfor q := range queues {\n\t\tqnames = append(qnames, q)\n\t}\n\tshutdownTimeout := cfg.ShutdownTimeout\n\tif shutdownTimeout == 0 {\n\t\tshutdownTimeout = defaultShutdownTimeout\n\t}\n\thealthcheckInterval := cfg.HealthCheckInterval\n\tif healthcheckInterval == 0 {\n\t\thealthcheckInterval = defaultHealthCheckInterval\n\t}\n\t// TODO: Create a helper to check for zero value and fall back to default (e.g. getDurationOrDefault())\n\tgroupGracePeriod := cfg.GroupGracePeriod\n\tif groupGracePeriod == 0 {\n\t\tgroupGracePeriod = defaultGroupGracePeriod\n\t}\n\tif groupGracePeriod < time.Second {\n\t\tpanic(\"GroupGracePeriod cannot be less than a second\")\n\t}\n\tlogger := log.NewLogger(cfg.Logger)\n\tloglevel := cfg.LogLevel\n\tif loglevel == level_unspecified {\n\t\tloglevel = InfoLevel\n\t}\n\tlogger.SetLevel(toInternalLogLevel(loglevel))\n\n\trdb := rdb.NewRDB(c)\n\tstarting := make(chan *workerInfo)\n\tfinished := make(chan *base.TaskMessage)\n\tsyncCh := make(chan *syncRequest)\n\tsrvState := &serverState{value: srvStateNew}\n\tcancels := base.NewCancelations()\n\n\tsyncer := newSyncer(syncerParams{\n\t\tlogger:     logger,\n\t\trequestsCh: syncCh,","sourceCodeStart":479,"sourceCodeEnd":515,"githubUrl":"https://github.com/hibiken/asynq/blob/d135f1439bee74e989b7f9b41ecd542cc87f024a/server.go#L479-L515","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Set cfg.GroupGracePeriod to at least 1*time.Second (e.g. 10*time.Second).","If you want the default, leave GroupGracePeriod as 0 (zero value) instead of a small duration.","Validate configuration before constructing the server: reject sub-second values with a clear error message.","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)."],"exampleFix":"// before\ncfg := asynq.Config{ GroupGracePeriod: 500 * time.Millisecond }\nsrv := asynq.NewServer(redisOpt, cfg)\n// after\ncfg := asynq.Config{ GroupGracePeriod: 10 * time.Second }\nsrv := asynq.NewServer(redisOpt, cfg)","handlingStrategy":"validation","validationCode":"func validateGracePeriod(d time.Duration) error {\n    if d != 0 && d < time.Second {\n        return fmt.Errorf(\"GroupGracePeriod must be >= 1s (or 0 for default), got %s\", d)\n    }\n    return nil\n}","typeGuard":null,"tryCatchPattern":"// Go panics are not catchable via try/catch; optionally recover in main:\nfunc main() {\n    defer func() {\n        if r := recover(); r != nil {\n            log.Fatalf(\"invalid config: %v\", r)\n        }\n    }()\n    srv := asynq.NewServer(opt, cfg)\n    _ = srv\n}","preventionTips":["Validate all duration config fields at startup before constructing the server.","Parse durations with time.ParseDuration instead of raw integer conversions.","Remember zero means 'use default' — do not substitute a tiny duration for it.","Add a unit test asserting your config loader never emits sub-second nonzero durations."],"tags":["go","configuration","panic","duration","startup"],"backgroundTag":"invalid-config-value","analyzedSha":"d135f1439bee74e989b7f9b41ecd542cc87f024a","analyzedAt":"2026-09-07T19:02:34.660Z","contentChangedAt":"2026-09-07T19:02:34.660Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}