hibiken/asynq · error · ErrHandlerNotFound

%w %q

Error message

%w %q

What it means

NotFound is the handler asynq uses when a task's type has no registered handler in a ServeMux. It wraps ErrHandlerNotFound and includes the task type so developers can see which type is unregistered. The server will record this error and the task goes through normal error/retry handling.

Solutions

  1. Register the missing type on the ServeMux: mux.HandleFunc(task.Type(), handler)
  2. Fix the type-name mismatch — use shared constants like "email:welcome" between producer and consumer
  3. If unhandled types are expected, mount mux.Use(asynq.NotFoundHandler()) behavior deliberately or log-and-ack via a custom middleware

Example fix

// before
mux.HandleFunc("email:welcome", welcomeHandler) // task type is "email:remind"
// after
mux.HandleFunc("email:welcome", welcomeHandler)
mux.HandleFunc("email:remind", remindHandler)
Defensive patterns

Strategy: try-catch

Validate before calling

registered := map[string]bool{
    "email:welcome": true,
    "email:remind": true,
}
if !registered[t.Type()] {
    log.Printf("warning: task type %q has no handler registered", t.Type())
}

Type guard

func hasHandler(mux *asynq.ServeMux, taskType string) bool {
    return knownTypes[taskType]
}

Try / catch

if errors.Is(err, asynq.ErrHandlerNotFound) {
    log.Printf("unregistered task type %q; dropping or routing to DLQ", taskType)
    return nil // or forward for inspection
}

Prevention

When it happens

Trigger: Enqueuing/processing a task whose Type string was never registered via mux.HandleFunc/mux.Handle — typos in type names, task types added by newer producers but not by the consumer, or type strings built dynamically.

Common situations: Producer/consumer version skew where the producer enqueues a new task type the old worker doesn't know; refactoring renamed task constants on one side only; sharing type strings as raw literals instead of constants.

Understand the failure class

Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.

Related errors


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

Appendix: source

Thrown at servemux.go:156

// HandleFunc registers the handler function for the given pattern.
func (mux *ServeMux) HandleFunc(pattern string, handler func(context.Context, *Task) error) {
	if handler == nil {
		panic("asynq: nil handler")
	}
	mux.Handle(pattern, HandlerFunc(handler))
}

// Use appends a MiddlewareFunc to the chain.
// Middlewares are executed in the order that they are applied to the ServeMux.
func (mux *ServeMux) Use(mws ...MiddlewareFunc) {
	mux.mu.Lock()
	defer mux.mu.Unlock()
	mux.mws = append(mux.mws, mws...)
}

// NotFound returns an error indicating that the handler was not found for the given task.
func NotFound(ctx context.Context, task *Task) error {
	return fmt.Errorf("%w %q", ErrHandlerNotFound, task.Type())
}

// NotFoundHandler returns a simple task handler that returns a “not found“ error.
func NotFoundHandler() Handler { return HandlerFunc(NotFound) }

View on GitHub (pinned to d135f1439b)