Named Backreferences in Go: `\k<name>` Won't Compile — Here's the Fix
Go supports named capture groups but not \k<name> or (?P=name) references — the same RE2 limit, with a named-capture fix.
The errors that brought you here:
error parsing regexp: invalid escape sequence: `\k`or, if you came from Python syntax:
error parsing regexp: invalid or unsupported Perl syntax: `(?P`Here's the part that trips people up: Go happily supports named capture groups — (?P<name>...) compiles fine and re.SubexpIndex("name") gives you clean access to what it captured. What Go doesn't support is referring back to a named group inside the same pattern — JS's \k<name>, Python's (?P=name). Same engine, same reason as numbered backreferences: RE2 guarantees linear-time matching by compiling to finite automata, and any backreference — named or numbered — requires backtracking. The name is cosmetic; the mechanism is the deal-breaker.
The workaround: named captures + verify in code
Identical shape to the numbered-backreference fix, but the named groups keep the Go side readable.
Matching a doubled word, JS/Python style:
JS: \b(?<word>\w+)\s+\k<word>\b
Python: \b(?P<word>\w+)\s+(?P=word)\bGo — capture two named groups, compare in code:
re := regexp.MustCompile(`\b(?P<first>\w+)\s+(?P<second>\w+)\b`)
first, second := re.SubexpIndex("first"), re.SubexpIndex("second")
for _, m := range re.FindAllStringSubmatch(text, -1) {
if m[first] == m[second] {
fmt.Println("duplicate:", m[first])
}
}SubexpIndex (Go 1.15+) resolves the group name to its index once, so the loop body reads almost like the JS original. This is the whole trick: the regex loosens ("any two words"), and Go enforces the equality the backreference used to.
A worked example: paired quotes with named groups
The delimiter-pairing idiom — match a quote, then require the same quote to close:
JS: (?<q>['"]).*?\k<q>Go, capture-and-verify:
re := regexp.MustCompile(`(?P<q>['"]).*?(?P<close>['"])`)
qi, ci := re.SubexpIndex("q"), re.SubexpIndex("close")
for _, m := range re.FindAllStringSubmatch(input, -1) {
if m[qi] == m[ci] {
// properly paired
}
}But note the better answer from the numbered-backreference guide applies here too: "[^"]*"|'[^']*' needs no backreference at all. Before porting a backreference, always ask whether alternation can replace it outright — it often can, and the result runs in every engine.
Syntax cheat sheet — named groups across engines
| Define group | Reference in pattern | Reference in replacement | |
|---|---|---|---|
| JavaScript | (?<name>...) | \k<name> | $<name> |
| Python | (?P<name>...) | (?P=name) | \g<name> |
| Go (RE2) | (?P<name>...) | ❌ not supported | ${name} |
Two details worth knowing:
- Go accepts
(?P<name>...)(Python style); Go 1.22+ also accepts(?<name>...)(JS style). Defining groups is portable — referencing them mid-pattern is not. - Go does support named groups in replacement strings:
re.ReplaceAllString(s, "${name}")works fine. Only in-pattern backreferences are off the table. If your goal was rearranging text (swap(?P<last>\w+), (?P<first>\w+)intofirst last), you never needed an in-pattern backreference — plainReplaceAllStringdoes it:
re := regexp.MustCompile(`(?P<last>\w+), (?P<first>\w+)`)
out := re.ReplaceAllString("Ebert, Andrew", "${first} ${last}")
// "Andrew Ebert"A surprising number of "I need named backreferences" cases are actually this — replacement, not in-pattern reference — and port to Go untouched.
Test it live
Try named capture groups and the duplicate word pattern side by side in JS, Python, and Go's actual RE2 engine in RegexPro's tester.
Related guides: Backreferences in Go · Lookarounds in Go · Python named groups in JavaScript