hibiken/asynq · critical

asynq: invalid pattern

Error message

asynq: invalid pattern

What it means

ServeMux.Handle panics with "asynq: invalid pattern" when the task pattern string is empty or consists only of whitespace. Every registered handler must have a non-empty pattern identifying the task type it handles, so registering with "" (or " ") is treated as a programming error and panics rather than returning an error.

Solutions

  1. Pass a non-empty task type string, e.g. mux.Handle("email:welcome", handler)
  2. Validate config/env values supplying task type names before registering
  3. Add a startup guard that fails fast if any configured task type is empty
  4. Check constants used as patterns for accidental empty values

Example fix

// before
mux.Handle(cfg.TaskType, handler) // cfg.TaskType == ""

// after
if strings.TrimSpace(cfg.TaskType) == "" {
    log.Fatal("task type must be configured")
}
mux.Handle(cfg.TaskType, handler)
Defensive patterns

Strategy: validation

Validate before calling

if strings.TrimSpace(pattern) == "" {
    return errors.New("task pattern must be a non-empty string")
}
mux.Handle(pattern, handler)

Type guard

func validPattern(p string) bool { return strings.TrimSpace(p) != "" }

Try / catch

defer func() {
    if r := recover(); r != nil {
        log.Fatalf("mux registration failed: %v", r)
    }
}()
mux.Handle(pattern, handler)

Prevention

When it happens

Trigger: Calling mux.Handle("", handler) or mux.HandleFunc(" ", fn); building the pattern dynamically from an empty config value or environment variable; a task-type constant that was left uninitialized (empty string).

Common situations: Task type names read from config/flags that default to empty string; string concatenation producing an empty prefix; refactoring that removed the literal task type name from the Handle call.

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


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

Appendix: source

Thrown at servemux.go:106

	// Check for longest valid match.
	// mux.es contains all patterns from longest to shortest.
	for _, e := range mux.es {
		if strings.HasPrefix(typename, e.pattern) {
			return e.h, e.pattern
		}
	}
	return nil, ""

}

// Handle registers the handler for the given pattern.
// If a handler already exists for pattern, Handle panics.
func (mux *ServeMux) Handle(pattern string, handler Handler) {
	mux.mu.Lock()
	defer mux.mu.Unlock()

	if strings.TrimSpace(pattern) == "" {
		panic("asynq: invalid pattern")
	}
	if handler == nil {
		panic("asynq: nil handler")
	}
	if _, exist := mux.m[pattern]; exist {
		panic("asynq: multiple registrations for " + pattern)
	}

	if mux.m == nil {
		mux.m = make(map[string]muxEntry)
	}
	e := muxEntry{h: handler, pattern: pattern}
	mux.m[pattern] = e
	mux.es = appendSorted(mux.es, e)
}

func appendSorted(es []muxEntry, e muxEntry) []muxEntry {
	n := len(es)

View on GitHub (pinned to d135f1439b)