hibiken/asynq · error
handler not set
Error message
handler not set
What it means
This is the placeholder error returned by the processor's default handler when no real Handler was configured on the server. It surfaces at task-processing time: tasks are consumed and immediately fail because the server was created without a handler.
Solutions
- Call server.SetHandler(mux) (or set Handler) before starting the server
- Verify the handler assignment happens on the same Server instance you Run
- Register task types on the mux so tasks match a handler instead of hitting defaults
Example fix
// before
srv.Run(deferred) // handler never set
// after
mux := asynq.NewServeMux()
mux.HandleFunc("email:welcome", welcomeHandler)
srv.SetHandler(mux)
srv.Run(deferred) Defensive patterns
Strategy: validation
Validate before calling
if srvHandler == nil {
return errors.New("asynq server started without a handler; call SetHandler before Run")
} Try / catch
// at run time, treat this as fatal config bug
if err := taskProcessingErr; err != nil && strings.Contains(err.Error(), "handler not set") {
log.Fatal("server misconfigured: no handler set")
} Prevention
- Always pair Server creation with mux creation and srv.SetHandler(mux) in the same bootstrap function
- Add an integration test that runs one task end-to-end to catch missing handlers
- Keep bootstrap code in one place to avoid partial setup paths
When it happens
Trigger: Creating a Server and calling Run/ProcessTasks without SetHandler (or NewServerFromRedisClient/test constructors leaving params.handler nil so newProcessor installs the placeholder). The placeholder HandlerFunc returns fmt.Errorf("handler not set") for every task.
Common situations: Forgetting server.SetHandler(mux) before Run; wiring a ServeMux but never assigning it to the server; copy-pasted bootstrap code missing the SetHandler line.
Understand the failure class
Background: "X is required", "must be set", "cannot be empty": the missing-required-config error family, from Vertex AI project/location to WeChat keys — this error's family across 18 libraries.
Related errors
- %w %q
- batch enqueue does not support group tasks
- batch enqueue does not support unique tasks
- redis connection is shared so the Inspector can't be closed…
- asynq
AI-assisted analysis of hibiken/asynq@d135f1439b (2026-09-07).
Data as JSON: /api/errors/c455a5cce2fbf3a4.
Report an issue: GitHub.
Appendix: source
Thrown at processor.go:118
return &processor{
logger: params.logger,
broker: params.broker,
baseCtxFn: params.baseCtxFn,
clock: timeutil.NewRealClock(),
queueConfig: queues,
orderedQueues: orderedQueues,
taskCheckInterval: params.taskCheckInterval,
retryDelayFunc: params.retryDelayFunc,
isFailureFunc: params.isFailureFunc,
syncRequestCh: params.syncCh,
cancelations: params.cancelations,
errLogLimiter: rate.NewLimiter(rate.Every(3*time.Second), 1),
sema: make(chan struct{}, params.concurrency),
done: make(chan struct{}),
quit: make(chan struct{}),
abort: make(chan struct{}),
errHandler: params.errHandler,
handler: HandlerFunc(func(ctx context.Context, t *Task) error { return fmt.Errorf("handler not set") }),
shutdownTimeout: params.shutdownTimeout,
starting: params.starting,
finished: params.finished,
}
}
// Note: stops only the "processor" goroutine, does not stop workers.
// It's safe to call this method multiple times.
func (p *processor) stop() {
p.once.Do(func() {
p.logger.Debug("Processor shutting down...")
// Unblock if processor is waiting for sema token.
close(p.quit)
// Signal the processor goroutine to stop processing tasks
// from the queue.
p.done <- struct{}{}
})
}View on GitHub (pinned to d135f1439b)