charmbracelet/vhs · error · parser.Error
Invalid regular expression '${literal}': ${err}
Error message
Invalid regular expression '${literal}': ${err} What it means
Wait (and Wait+) can take a regular expression to match against the terminal output. vhs compiles the regex at parse time with regexp.Compile and reports both the offending pattern and the underlying regexp/syntax error if it is invalid. The tape cannot proceed with a regex Go cannot compile.
Source
Thrown at parser/parser.go:224
cmd.Args = "Line"
}
cmd.Options = p.parseSpeed()
if cmd.Options != "" {
dur, _ := time.ParseDuration(cmd.Options)
if dur <= 0 {
p.errors = append(p.errors, NewError(p.peek, "Wait expects positive duration"))
return cmd
}
}
if p.peek.Type != token.REGEX {
// fallback to default
return cmd
}
p.nextToken()
if _, err := regexp.Compile(p.cur.Literal); err != nil {
p.errors = append(p.errors, NewError(p.cur, fmt.Sprintf("Invalid regular expression '%s': %v", p.cur.Literal, err)))
return cmd
}
cmd.Args += " " + p.cur.Literal
return cmd
}
// parseSpeed parses a typing speed indication.
//
// i.e. @<time>
//
// This is optional (defaults to 100ms), thus skips (rather than error-ing)
// if the typing speed is not specified.
func (p *Parser) parseSpeed() string {
if p.peek.Type == token.AT {
p.nextToken()
return p.parseTime()View on GitHub (pinned to c073383b5d)
Solutions
- Fix the regex syntax reported in the error (balance parens/brackets, escape special chars with \\).
- Validate the pattern in a Go regexp tester (RE2-flavored) before putting it in the tape.
- Simplify to a literal substring if no regex features are needed.
Example fix
// before (.tape) Wait /foo(bar/ // after (.tape) Wait /foo\(bar\)/
Defensive patterns
Strategy: validation
Validate before calling
func validRegex(p string) error { _, err := regexp.Compile(p); return err } Prevention
- Pre-compile the regex with Go's regexp package (RE2) to validate syntax
- Escape parentheses and brackets; avoid backreference syntax RE2 rejects
- Keep patterns simple — prefer literal substrings when possible
When it happens
Trigger: A REGEX token follows Wait and regexp.Compile(p.cur.Literal) fails, e.g. 'Wait /foo(/' (unclosed group), 'Wait /[a-z/' (bad character class), or a stray unescaped '*' or '+' at pattern start.
Common situations: Hand-written regexes with unbalanced parentheses or brackets; regexes escaped for the shell instead of Go regexp (RE2) syntax; patterns copied from grep/sed that use features RE2 rejects or mis-escapes.
Related errors
- Wait+ expects Line or Screen
- Wait expects positive duration
- Invalid command: ${literal}
- Expected time after ${literal}
- Modifiers must come before other characters
AI-assisted analysis of charmbracelet/vhs@c073383b5d (2026-09-02).
Data as JSON: /api/errors/484d40c7649558b9.
Report an issue: GitHub.