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

  1. Fix the regex syntax reported in the error (balance parens/brackets, escape special chars with \\).
  2. Validate the pattern in a Go regexp tester (RE2-flavored) before putting it in the tape.
  3. 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

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


AI-assisted analysis of charmbracelet/vhs@c073383b5d (2026-09-02). Data as JSON: /api/errors/484d40c7649558b9. Report an issue: GitHub.