{"id":"8cd03d83e7e26ce1","repo":"gohugoio/hugo","slug":"redirects-must-have-either-from-or-fromre-set","errorCode":null,"errorMessage":"redirects must have either From or FromRe set","messagePattern":"redirects must have either From or FromRe set","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"config/commonConfig.go","lineNumber":262,"sourceCode":"\t\t}\n\t}\n\treturn true\n}\n\nfunc (s *Server) CompileConfig(logger loggers.Logger) error {\n\tif s.compiledHeaders != nil {\n\t\treturn nil\n\t}\n\tfor _, h := range s.Headers {\n\t\tg, err := glob.Compile(h.For)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"failed to compile Headers glob %q: %w\", h.For, err)\n\t\t}\n\t\ts.compiledHeaders = append(s.compiledHeaders, g)\n\t}\n\tfor _, r := range s.Redirects {\n\t\tif r.From == \"\" && r.FromRe == \"\" {\n\t\t\treturn fmt.Errorf(\"redirects must have either From or FromRe set\")\n\t\t}\n\t\trd := redirect{\n\t\t\theaders: make(map[string]glob.Glob),\n\t\t}\n\t\tif r.From != \"\" {\n\t\t\tg, err := glob.Compile(r.From)\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"failed to compile Redirect glob %q: %w\", r.From, err)\n\t\t\t}\n\t\t\trd.from = g\n\t\t}\n\t\tif r.FromRe != \"\" {\n\t\t\tre, err := regexp.Compile(r.FromRe)\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"failed to compile Redirect regexp %q: %w\", r.FromRe, err)\n\t\t\t}\n\t\t\trd.fromRe = re\n\t\t}","sourceCodeStart":244,"sourceCodeEnd":280,"githubUrl":"https://github.com/gohugoio/hugo/blob/8a468df065a75c1c7cf9f6850f32148746590ea5/config/commonConfig.go#L244-L280","documentation":"Raised by Server.CompileConfig when a `[[server.redirects]]` entry defines neither `from` (glob) nor `fromRe` (regexp). A redirect with no source pattern can never match anything, so Hugo fails fast at config compile time instead of silently ignoring the rule.","triggerScenarios":"Declaring a `[[server.redirects]]` block containing only `to`/`status`/`force` keys, or misspelling the source key (e.g. `From = \"...\"` typoed as `form`, or using `source =` from Netlify syntax), leaving both From and FromRe empty when the map is decoded.","commonSituations":"Porting `_redirects`/netlify.toml rules (which use `from`/`source` differently) into Hugo's server config; leaving a placeholder redirect block while scaffolding; YAML/TOML indentation errors that detach `from` from the redirect entry; upgrading configs where `fromRe` was added (Hugo 0.112+) and mixing up key names.","solutions":["Add a `from` glob (e.g. `from = \"/old/**\"`) or a `fromRe` regexp to every `[[server.redirects]]` entry.","Check for typos in the key name — it must be exactly `from` or `fromRe` (case-insensitive in TOML decoding).","Delete empty placeholder redirect blocks from the config."],"exampleFix":"# before (hugo.toml)\n[[server.redirects]]\nto = \"/new/\"\nstatus = 301\n\n# after\n[[server.redirects]]\nfrom = \"/old/**\"\nto = \"/new/\"\nstatus = 301","handlingStrategy":"validation","validationCode":"for i, r := range cfg.Server.Redirects {\n    if r.From == \"\" && r.FromRe == \"\" {\n        return fmt.Errorf(\"server.redirects[%d]: set either 'from' (glob) or 'fromRe' (regexp)\", i)\n    }\n}","typeGuard":null,"tryCatchPattern":"if err := cfg.Server.CompileConfig(logger); err != nil {\n    if strings.Contains(err.Error(), \"redirects must have either From or FromRe\") {\n        // a [[server.redirects]] block is missing its matcher key\n    }\n    return err\n}","preventionTips":["Every [[server.redirects]] entry needs `from` or `fromRe` — watch for typos like `From =` vs `from =` in the wrong table, or an empty string","When converting Netlify _redirects to hugo.toml, verify each rule got a matcher","Keep redirects in one place and validate with `hugo server` after edits"],"tags":["hugo","config","server","redirects","validation"],"analyzedSha":"8a468df065a75c1c7cf9f6850f32148746590ea5","analyzedAt":"2026-07-31T21:23:07.045Z","schemaVersion":2}