{"record":{"id":"2a0b57d7d72254f6","repo":"hibiken/asynq","slug":"asynq-invalid-pattern","errorCode":null,"errorMessage":"asynq: invalid pattern","messagePattern":"asynq: invalid pattern","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"servemux.go","lineNumber":106,"sourceCode":"\t// Check for longest valid match.\n\t// mux.es contains all patterns from longest to shortest.\n\tfor _, e := range mux.es {\n\t\tif strings.HasPrefix(typename, e.pattern) {\n\t\t\treturn e.h, e.pattern\n\t\t}\n\t}\n\treturn nil, \"\"\n\n}\n\n// Handle registers the handler for the given pattern.\n// If a handler already exists for pattern, Handle panics.\nfunc (mux *ServeMux) Handle(pattern string, handler Handler) {\n\tmux.mu.Lock()\n\tdefer mux.mu.Unlock()\n\n\tif strings.TrimSpace(pattern) == \"\" {\n\t\tpanic(\"asynq: invalid pattern\")\n\t}\n\tif handler == nil {\n\t\tpanic(\"asynq: nil handler\")\n\t}\n\tif _, exist := mux.m[pattern]; exist {\n\t\tpanic(\"asynq: multiple registrations for \" + pattern)\n\t}\n\n\tif mux.m == nil {\n\t\tmux.m = make(map[string]muxEntry)\n\t}\n\te := muxEntry{h: handler, pattern: pattern}\n\tmux.m[pattern] = e\n\tmux.es = appendSorted(mux.es, e)\n}\n\nfunc appendSorted(es []muxEntry, e muxEntry) []muxEntry {\n\tn := len(es)","sourceCodeStart":88,"sourceCodeEnd":124,"githubUrl":"https://github.com/hibiken/asynq/blob/d135f1439bee74e989b7f9b41ecd542cc87f024a/servemux.go#L88-L124","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","solutions":["Pass a non-empty task type string, e.g. mux.Handle(\"email:welcome\", handler)","Validate config/env values supplying task type names before registering","Add a startup guard that fails fast if any configured task type is empty","Check constants used as patterns for accidental empty values"],"exampleFix":"// before\nmux.Handle(cfg.TaskType, handler) // cfg.TaskType == \"\"\n\n// after\nif strings.TrimSpace(cfg.TaskType) == \"\" {\n    log.Fatal(\"task type must be configured\")\n}\nmux.Handle(cfg.TaskType, handler)","handlingStrategy":"validation","validationCode":"if strings.TrimSpace(pattern) == \"\" {\n    return errors.New(\"task pattern must be a non-empty string\")\n}\nmux.Handle(pattern, handler)","typeGuard":"func validPattern(p string) bool { return strings.TrimSpace(p) != \"\" }","tryCatchPattern":"defer func() {\n    if r := recover(); r != nil {\n        log.Fatalf(\"mux registration failed: %v\", r)\n    }\n}()\nmux.Handle(pattern, handler)","preventionTips":["Define task-type names as constants, never build them from possibly-empty config without validation","Validate config/env-sourced task names at startup","Search for mux.Handle calls with non-literal patterns and audit them","Fail fast in an init test registering all expected patterns"],"tags":["panic","routing","validation","go"],"backgroundTag":"empty-required-field","analyzedSha":"d135f1439bee74e989b7f9b41ecd542cc87f024a","analyzedAt":"2026-09-07T19:02:34.660Z","contentChangedAt":"2026-09-07T19:02:34.660Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}