kataras/iris · error
redirect match: loop detected: pattern: %s vs target: %s
Error message
redirect match: loop detected: pattern: %s vs target: %s
What it means
The rewrite parser rejects redirect rules whose regex pattern also matches its own target, because that creates an infinite redirect loop: once the client is sent to the target, the rule would match it again. The check compiles the pattern and tests it against the target string, failing with this error when they overlap.
Source
Thrown at middleware/rewrite/rewrite.go:304
codeStr, pattern, target := parts[0], parts[1], parts[2]
for i, ch := range codeStr {
if !isDigit(ch) {
return nil, fmt.Errorf("redirect match: status code digits: %s [%d:%c]", codeStr, i, ch)
}
}
code, err := strconv.Atoi(codeStr)
if err != nil {
// this should not happen, we check abt digit
// and correctly position the error too but handle it.
return nil, fmt.Errorf("redirect match: status code digits: %s: %v", codeStr, err)
}
regex := regexp.MustCompile(pattern)
if regex.MatchString(target) {
return nil, fmt.Errorf("redirect match: loop detected: pattern: %s vs target: %s", pattern, target)
}
v := &redirectMatch{
code: code,
pattern: regex,
target: target,
noRedirect: code <= 0,
isRelativePattern: pattern[0] == '/', // search by path.
}
return v, nil
}
func isDigit(ch rune) bool {
return '0' <= ch && ch <= '9'
}
func getPort(hostport string) string { // returns :port, note that this is only called on non-loopbacks.View on GitHub (pinned to 7bedaf55a0)
Solutions
- Make the pattern more specific so it cannot match the target, e.g. anchor it: '^/old/(.*)$'.
- Change the target to a path outside the pattern's domain (different prefix or TLD).
- Use a negative-construct or narrower regex that excludes the destination path.
- Split broad rules into several narrow rules whose targets do not self-match.
Example fix
// before "301 /(.*) /new/$1" // pattern matches /new/... target -> loop // after "301 ^/old/(.*)$ /new/$1"
Defensive patterns
Strategy: validation
Validate before calling
re := regexp.MustCompile(pattern)
if re.MatchString(target) {
return fmt.Errorf("redirect rule %q would loop: target %q matches pattern", pattern, target)
} Try / catch
if err := rewrite.New(lines); err != nil {
if strings.Contains(err.Error(), "loop detected") {
log.Fatalf("fix redirect pattern %v", err)
}
} Prevention
- Anchor redirect patterns (^...$) so they can't match their own targets.
- Send redirects to a different path prefix than the one matched.
- Test each rule with the pattern.MatchString(target) check before deploying.
When it happens
Trigger: Calling rewrite.New with a line like '301 /(.*) /$1' or any pattern that matches the replacement target, e.g. pattern '/blog' with target '/blog/new'.
Common situations: Broad catch-all redirect patterns (match everything) combined with targets that retain the matched path; migrating a site where old and new URLs share a prefix.
Related errors
- redirect match: invalid line: %s
- redirect match: status code digits: %s [%d:%c]
- redirect match: status code digits: %s: %v
- panic(err)
- panic(err)
AI-assisted analysis of kataras/iris@7bedaf55a0 (2026-08-30).
Data as JSON: /api/errors/9dfa2996809a7335.
Report an issue: GitHub.