hibiken/asynq · critical

asynq: server cannot run with nil handler

Error message

asynq: server cannot run with nil handler

What it means

Server.Start refuses to start the server when the Handler argument is nil. A nil handler means no task-processing function would be available, so tasks could never be executed; asynq fails fast at startup instead of panicking later when a task arrives.

Solutions

  1. Pass a non-nil asynq.Handler (e.g. a valid *asynq.ServeMux or custom handler) to Start.
  2. Check that your handler constructor never returns a nil interface; return an explicit error instead.
  3. Wrap the handler with asynq.HandlerFunc(func(ctx context.Context, t *asynq.Task) error { ... }) if none exists yet.

Example fix

// before
var h asynq.Handler
srv.Start(h) // h is nil
// after
mux := asynq.NewServeMux()
mux.HandleFunc("email:welcome", handleWelcomeTask)
srv.Start(mux)
Defensive patterns

Strategy: validation

Validate before calling

if handler == nil {
    return errors.New("cannot start asynq server: handler is nil")
}
return srv.Start(handler)

Type guard

func isNilHandler(h asynq.Handler) bool {
    return h == nil
}

Try / catch

if err := srv.Start(handler); err != nil {
    log.Fatalf("asynq start failed: %v", err)
}

Prevention

When it happens

Trigger: Calling srv.Start(nil), or Start with a variable of interface type Handler whose underlying value is nil (e.g. an uninitialized handler struct pointer assigned to the interface).

Common situations: Handler construction failed earlier but the code ignores the error; a factory returns nil on some config path; handler wiring skipped in tests or scaffolding code.

Related errors


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

Appendix: source

Thrown at server.go:682

	if err := srv.Start(handler); err != nil {
		return err
	}
	srv.waitForSignals()
	srv.Shutdown()
	return nil
}

// Start starts the worker server. Once the server has started,
// it pulls tasks off queues and starts a worker goroutine for each task
// and then call Handler to process it.
// Tasks are processed concurrently by the workers up to the number of
// concurrency specified in Config.Concurrency.
//
// Start returns any error encountered at server startup time.
// If the server has already been shutdown, ErrServerClosed is returned.
func (srv *Server) Start(handler Handler) error {
	if handler == nil {
		return fmt.Errorf("asynq: server cannot run with nil handler")
	}
	srv.processor.handler = handler

	if err := srv.start(); err != nil {
		return err
	}
	srv.logger.Info("Starting processing")

	srv.heartbeater.start(&srv.wg)
	srv.healthchecker.start(&srv.wg)
	srv.subscriber.start(&srv.wg)
	srv.syncer.start(&srv.wg)
	srv.recoverer.start(&srv.wg)
	srv.forwarder.start(&srv.wg)
	srv.processor.start(&srv.wg)
	srv.janitor.start(&srv.wg)
	srv.aggregator.start(&srv.wg)
	return nil

View on GitHub (pinned to d135f1439b)