{"record":{"id":"f82a80f438db58b8","repo":"charmbracelet/vhs","slug":"wait-expects-positive-duration","errorCode":null,"errorMessage":"Wait expects positive duration","messagePattern":"Wait expects positive duration","errorType":"validation","errorClass":"parser.Error","httpStatus":null,"severity":"error","filePath":"parser/parser.go","lineNumber":213,"sourceCode":"\tcmd := Command{Type: token.WAIT}\n\n\tif p.peek.Type == token.PLUS {\n\t\tp.nextToken()\n\t\tif p.peek.Type != token.STRING || (p.peek.Literal != \"Line\" && p.peek.Literal != \"Screen\") {\n\t\t\tp.errors = append(p.errors, NewError(p.peek, \"Wait+ expects Line or Screen\"))\n\t\t\treturn cmd\n\t\t}\n\t\tcmd.Args = p.peek.Literal\n\t\tp.nextToken()\n\t} else {\n\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}","sourceCodeStart":195,"sourceCodeEnd":231,"githubUrl":"https://github.com/charmbracelet/vhs/blob/c073383b5de0b1f57bf514113029c306bc986539/parser/parser.go#L195-L231","documentation":"When a Wait command is followed by '@<duration>' (parsed via parseSpeed into Options), vhs validates the duration with time.ParseDuration and requires it to be strictly positive. This error fires when the Wait timeout is zero or negative, e.g. 'Wait@0s' or 'Wait@-1s'.","triggerScenarios":"Any 'Wait@<duration>' where time.ParseDuration returns a value <= 0: 'Wait@0', 'Wait@0s', 'Wait@-500ms', or an unparseable value that yields dur <= 0 (parse error is swallowed by the '_').","commonSituations":"Setting a zero timeout to mean 'no timeout' (vhs does not accept 0 here); accidentally using a negative duration; typos in unit suffix causing the duration to parse to 0.","solutions":["Use a positive duration, e.g. 'Wait@5s' or 'Wait@500ms'.","Remove the '@duration' clause entirely to use the default timeout.","Check unit suffixes are valid Go durations (ms, s, m, h)."],"exampleFix":"// before (.tape)\nWait@0s Screen\n// after (.tape)\nWait@5s Screen","handlingStrategy":"validation","validationCode":"func isPositiveDuration(d string) bool { dur, err := time.ParseDuration(strings.TrimPrefix(d, \"@\")); return err == nil && dur > 0 }","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always pair Wait@ with a positive Go-style duration (500ms, 5s, 1m)","Never use Wait@0 — omit the @ clause for defaults","Test durations locally before committing tapes"],"tags":["parser","wait-command","duration"],"backgroundTag":"invalid-duration","analyzedSha":"c073383b5de0b1f57bf514113029c306bc986539","analyzedAt":"2026-09-02T01:53:05.682Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-09T06:17:21.866Z"}