{"record":{"id":"ec5ab46019859781","repo":"kataras/iris","slug":"iris-switch-hosts-loop-detected-between-express","errorCode":null,"errorMessage":"iris: switch: hosts: loop detected between expression: \"%s\" and target host: \"%s\"","messagePattern":"iris: switch: hosts: loop detected between expression: \"(.+?)\" and target host: \"(.+?)\"","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"apps/switch_hosts.go","lineNumber":109,"sourceCode":"\tcase context.Application:\n\t\treturn target.(*iris.Application)\n\tcase string:\n\t\t// Check if the given target is an application name, if so\n\t\t// we must not redirect (loop) we must serve the request\n\t\t// using that app.\n\t\tif targetApp, ok := context.GetApplication(target); ok {\n\t\t\t// It's always iris.Application so we are totally safe here.\n\t\t\treturn targetApp.(*iris.Application)\n\t\t}\n\t\t// If it's a real host, warn the user of invalid input.\n\t\tu, err := url.Parse(target)\n\t\tif err == nil && u.IsAbs() {\n\t\t\t// remember, we redirect hosts, not full URLs here.\n\t\t\tpanic(fmt.Sprintf(`iris: switch: hosts: invalid target host: \"%s\"`, target))\n\t\t}\n\n\t\tif regex := regexp.MustCompile(host.Pattern); regex.MatchString(target) {\n\t\t\tpanic(fmt.Sprintf(`iris: switch: hosts: loop detected between expression: \"%s\" and target host: \"%s\"`, host.Pattern, host.Target))\n\t\t}\n\n\t\treturn newHostRedirectApp(target, HostsRedirectCode)\n\tdefault:\n\t\tpanic(fmt.Sprintf(\"iris: switch: hosts: invalid target type: %T\", target))\n\t}\n}\n\nfunc hostFilter(expr string) iris.Filter {\n\tregex := regexp.MustCompile(expr)\n\treturn func(ctx iris.Context) bool {\n\t\treturn regex.MatchString(ctx.Host())\n\t}\n}\n\n// HostsRedirectCode is the default status code is used\n// to redirect a matching host to a url.\nvar HostsRedirectCode = iris.StatusMovedPermanently","sourceCodeStart":91,"sourceCodeEnd":127,"githubUrl":"https://github.com/kataras/iris/blob/7bedaf55a0b64bbb2248a5845a2c60d81a30996a/apps/switch_hosts.go#L91-L127","documentation":"While resolving each host case, hostApp checks whether the target host matches the host's own pattern regex. If it does, registering the case would create an infinite redirect loop (the host redirects to itself), so it panics with a message naming both the expression and the target. This is a startup-time safety check for redirect cycles.","triggerScenarios":"Configuring a Hosts entry where the pattern (e.g. \"^(.*\\.)?example.com$\") also matches its own target (e.g. target \"www.example.com\" under a pattern matching example.com subdomains).","commonSituations":"Wildcard patterns like \".*\" or overly broad regexes paired with a target in the same domain; www → apex redirects written in the wrong direction; config refactors that copy a pattern across entries.","solutions":["Narrow the host pattern so it excludes the target host","Point the target at a different domain than the pattern matches","Swap the redirect direction (apex → www instead of www → apex) if that was the intent"],"exampleFix":"// before\nhosts := iris.Hosts{\"^(.*\\\\.)?example.com$\": \"www.example.com\"}\n// after\nhosts := iris.Hosts{\"^example\\\\.com$\": \"www.example.com\"}","handlingStrategy":"validation","validationCode":"for pattern, target := range hosts {\n\tif regexp.MustCompile(pattern).MatchString(target) {\n\t\treturn fmt.Errorf(\"host pattern %q matches its own target %q\", pattern, target)\n\t}\n}","typeGuard":null,"tryCatchPattern":"defer func() {\n\tif r := recover(); r != nil {\n\t\tif s, ok := r.(string); ok && strings.Contains(s, \"loop detected\") {\n\t\t\tlog.Fatalf(\"host redirect loop: %s\", s)\n\t\t}\n\t\tpanic(r)\n\t}\n}()","preventionTips":["Test each host pattern regex against its target before startup","Avoid overly broad patterns like \".*\"","Prefer anchored patterns (^...$) that exclude the target domain"],"tags":["iris","panic","redirect-loop","regex"],"backgroundTag":"redirect-loop-detected","analyzedSha":"7bedaf55a0b64bbb2248a5845a2c60d81a30996a","analyzedAt":"2026-08-30T20:38:16.250Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}