hibiken/asynq · error
task ID cannot be empty
Error message
task ID cannot be empty
What it means
A sentinel-less validation error raised in composeOptions when the taskIDOption value is blank (only whitespace). The client refuses to enqueue a task whose TaskID option carries an empty string, since an empty ID would be meaningless for conflict detection.
Solutions
- Validate the ID is non-blank before passing it to asynq.TaskID.
- Skip the TaskID option entirely when no ID is available.
- Fix the upstream source (struct field, DB lookup) producing the empty ID.
Example fix
// before
client.Enqueue(task, asynq.TaskID(cfg.TaskID))
// after
opts := []asynq.Option{}
if strings.TrimSpace(cfg.TaskID) != "" {
opts = append(opts, asynq.TaskID(cfg.TaskID))
}
client.Enqueue(task, opts...) Defensive patterns
Strategy: validation
Validate before calling
if strings.TrimSpace(id) == "" { return errors.New("task ID must be non-empty") } Type guard
func validTaskID(id string) bool { return strings.TrimSpace(id) != "" } Try / catch
// Validation-preferred; if hit anyway:
if err != nil && strings.Contains(err.Error(), "task ID cannot be empty") { /* fix caller data */ } Prevention
- Always trim/validate IDs sourced from external data before passing to asynq.TaskID
- Skip the TaskID option when no valid ID exists
- Add unit tests for enqueue paths with empty-ID inputs
When it happens
Trigger: Calling client.Enqueue(task, asynq.TaskID("")) or TaskID(" "), typically because a variable holding the ID was empty at call time.
Common situations: Building TaskID from a struct field or DB column that was unset/empty; string formatting that produced an empty ID; forgetting to validate user-supplied IDs before enqueueing.
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
- task already exists
- task ID conflicts with another task
- Unique TTL cannot be less than 1s
- group key cannot be empty
- task already exists
AI-assisted analysis of hibiken/asynq@d135f1439b (2026-09-07).
Data as JSON: /api/errors/b12e05770d376532.
Report an issue: GitHub.
Appendix: source
Thrown at client.go:295
timeout: 0, // do not set to defaultTimeout here
deadline: time.Time{},
processAt: time.Now(),
headers: make(map[string]string),
}
for _, opt := range opts {
switch opt := opt.(type) {
case retryOption:
res.retry = int(opt)
case queueOption:
qname := string(opt)
if err := base.ValidateQueueName(qname); err != nil {
return option{}, err
}
res.queue = qname
case taskIDOption:
id := string(opt)
if isBlank(id) {
return option{}, errors.New("task ID cannot be empty")
}
res.taskID = id
case timeoutOption:
res.timeout = time.Duration(opt)
case deadlineOption:
res.deadline = time.Time(opt)
case uniqueOption:
ttl := time.Duration(opt)
if ttl < 1*time.Second {
return option{}, errors.New("Unique TTL cannot be less than 1s")
}
res.uniqueTTL = ttl
case processAtOption:
res.processAt = time.Time(opt)
case processInOption:
res.processAt = time.Now().Add(time.Duration(opt))
case retentionOption:
res.retention = time.Duration(opt)View on GitHub (pinned to d135f1439b)