{"record":{"id":"484d40c7649558b9","repo":"charmbracelet/vhs","slug":"invalid-regular-expression-literal-err","errorCode":null,"errorMessage":"Invalid regular expression '${literal}': ${err}","messagePattern":"Invalid regular expression '(.+?)': (.+?)","errorType":"validation","errorClass":"parser.Error","httpStatus":null,"severity":"error","filePath":"parser/parser.go","lineNumber":224,"sourceCode":"\t\tcmd.Args = \"Line\"\n\t}\n\n\tcmd.Options = p.parseSpeed()\n\tif cmd.Options != \"\" {\n\t\tdur, _ := time.ParseDuration(cmd.Options)\n\t\tif dur <= 0 {\n\t\t\tp.errors = append(p.errors, NewError(p.peek, \"Wait expects positive duration\"))\n\t\t\treturn cmd\n\t\t}\n\t}\n\n\tif p.peek.Type != token.REGEX {\n\t\t// fallback to default\n\t\treturn cmd\n\t}\n\tp.nextToken()\n\tif _, err := regexp.Compile(p.cur.Literal); err != nil {\n\t\tp.errors = append(p.errors, NewError(p.cur, fmt.Sprintf(\"Invalid regular expression '%s': %v\", p.cur.Literal, err)))\n\t\treturn cmd\n\t}\n\n\tcmd.Args += \" \" + p.cur.Literal\n\n\treturn cmd\n}\n\n// parseSpeed parses a typing speed indication.\n//\n// i.e. @<time>\n//\n// This is optional (defaults to 100ms), thus skips (rather than error-ing)\n// if the typing speed is not specified.\nfunc (p *Parser) parseSpeed() string {\n\tif p.peek.Type == token.AT {\n\t\tp.nextToken()\n\t\treturn p.parseTime()","sourceCodeStart":206,"sourceCodeEnd":242,"githubUrl":"https://github.com/charmbracelet/vhs/blob/c073383b5de0b1f57bf514113029c306bc986539/parser/parser.go#L206-L242","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before (.tape)\nWait /foo(bar/\n// after (.tape)\nWait /foo\\(bar\\)/","handlingStrategy":"validation","validationCode":"func validRegex(p string) error { _, err := regexp.Compile(p); return err }","typeGuard":null,"tryCatchPattern":null,"preventionTips":["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"],"tags":["parser","wait-command","regex"],"backgroundTag":"invalid-regex","analyzedSha":"c073383b5de0b1f57bf514113029c306bc986539","analyzedAt":"2026-09-02T01:53:05.682Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-09T06:17:21.866Z"}