{"id":"dfef5ed84cfda522","repo":"gohugoio/hugo","slug":"failed-to-match-regex-pattern-against-string-w","errorCode":null,"errorMessage":"failed to match regex pattern against string: %w","messagePattern":"failed to match regex pattern against string: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"tpl/strings/strings.go","lineNumber":88,"sourceCode":"// RuneCount returns the number of runes in s.\nfunc (ns *Namespace) RuneCount(s any) (int, error) {\n\tss, err := cast.ToStringE(s)\n\tif err != nil {\n\t\treturn 0, fmt.Errorf(\"failed to convert content to string: %w\", err)\n\t}\n\treturn utf8.RuneCountInString(ss), nil\n}\n\n// CountWords returns the approximate word count in s.\nfunc (ns *Namespace) CountWords(s any) (int, error) {\n\tss, err := cast.ToStringE(s)\n\tif err != nil {\n\t\treturn 0, fmt.Errorf(\"failed to convert content to string: %w\", err)\n\t}\n\n\tisCJKLanguage, err := regexp.MatchString(`\\p{Han}|\\p{Hangul}|\\p{Hiragana}|\\p{Katakana}`, ss)\n\tif err != nil {\n\t\treturn 0, fmt.Errorf(\"failed to match regex pattern against string: %w\", err)\n\t}\n\n\tif !isCJKLanguage {\n\t\treturn len(strings.Fields(tpl.StripHTML(ss))), nil\n\t}\n\n\tcounter := 0\n\tfor word := range strings.FieldsSeq(tpl.StripHTML(ss)) {\n\t\truneCount := utf8.RuneCountInString(word)\n\t\tif len(word) == runeCount {\n\t\t\tcounter++\n\t\t} else {\n\t\t\tcounter += runeCount\n\t\t}\n\t}\n\n\treturn counter, nil\n}","sourceCodeStart":70,"sourceCodeEnd":106,"githubUrl":"https://github.com/gohugoio/hugo/blob/8a468df065a75c1c7cf9f6850f32148746590ea5/tpl/strings/strings.go#L70-L106","documentation":"Inside `strings.CountWords`, Hugo runs `regexp.MatchString` with a fixed CJK-detection pattern (`\\p{Han}|\\p{Hangul}|\\p{Hiragana}|\\p{Katakana}`) to decide between whitespace-based and rune-based word counting. This error wraps a failure from that match. Since the pattern is a hard-coded valid regex, `regexp.MatchString` compiling/matching it essentially cannot fail in practice — this branch is defensive and nearly unreachable in released Hugo versions.","triggerScenarios":"Only producible if the compiled-in regex were invalid (a source modification or a corrupted/forked build) — `regexp.MatchString` returns an error solely on pattern compile failure, and the input string cannot cause one. In stock Hugo, `{{ countwords .Content }}` will not hit this.","commonSituations":"Custom Hugo forks or patches that altered the CJK pattern; extremely unlikely toolchain/regexp package regressions. If a user reports it on an official release, it indicates a corrupted binary or a bug worth reporting upstream.","solutions":["Verify you're running an official Hugo release (`hugo version`); reinstall/redownload the binary if it may be corrupted.","If using a fork or custom build, diff `tpl/strings/strings.go` against upstream — the CJK regex at strings.go:86 has likely been modified into an invalid pattern.","If reproducible on an official build, file a Hugo issue with the wrapped error text and a minimal site."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// pre-validate the pattern outside the hot path\n{{ $pattern := `\\d+` }}\n// in Go: if _, err := regexp.Compile(pattern); err != nil { reject }","typeGuard":null,"tryCatchPattern":"matches, err := ns.FindRE(pattern, input)\nif err != nil {\n    // invalid regex — fix the pattern literal, don't retry\n}","preventionTips":["Test regex patterns with `regexp.Compile`/regex101 before embedding in templates","Use backtick raw strings in templates so backslashes aren't double-escaped","Never build patterns from user/content data without escaping via `regexp.QuoteMeta` semantics"],"tags":["hugo","go-templates","strings","regex","defensive-check"],"analyzedSha":"8a468df065a75c1c7cf9f6850f32148746590ea5","analyzedAt":"2026-07-31T21:23:07.045Z","schemaVersion":2}