{"record":{"id":"865d67981f217df1","repo":"charmbracelet/vhs","slug":"expected-time-after-literal","errorCode":null,"errorMessage":"Expected time after ${literal}","messagePattern":"Expected time after (.+?)","errorType":"validation","errorClass":"parser.Error","httpStatus":null,"severity":"error","filePath":"parser/parser.go","lineNumber":273,"sourceCode":"\t\tcount := p.peek.Literal\n\t\tp.nextToken()\n\t\treturn count\n\t}\n\n\treturn \"1\"\n}\n\n// parseTime parses a time argument.\n//\n//\t<number>[ms]\nfunc (p *Parser) parseTime() string {\n\tvar t string\n\n\tif p.peek.Type == token.NUMBER {\n\t\tt = p.peek.Literal\n\t\tp.nextToken()\n\t} else {\n\t\tp.errors = append(p.errors, NewError(p.cur, \"Expected time after \"+p.cur.Literal))\n\t\treturn \"\"\n\t}\n\n\t// Allow TypingSpeed to have bare units (e.g. 50ms, 100ms)\n\tif p.peek.Type == token.MILLISECONDS || p.peek.Type == token.SECONDS || p.peek.Type == token.MINUTES {\n\t\tt += p.peek.Literal\n\t\tp.nextToken()\n\t} else {\n\t\tt += \"s\"\n\t}\n\n\treturn t\n}\n\n// parseCtrl parses a control command.\n// A control command takes one or multiples characters and/or modifiers to type while ctrl is held down.\n//\n//\tCtrl[+Alt][+Shift]+<char>","sourceCodeStart":255,"sourceCodeEnd":291,"githubUrl":"https://github.com/charmbracelet/vhs/blob/c073383b5de0b1f57bf514113029c306bc986539/parser/parser.go#L255-L291","documentation":"vhs's parseTime reads a duration value for commands that accept one (TypingSpeed via Set, Sleep, and the @speed suffix). It expects a NUMBER token first; if the next token is not a number it emits 'Expected time after <literal>', where <literal> is the command or unit token that expected a numeric argument.","triggerScenarios":"'Sleep ms' or 'Sleep s' (unit without a number), 'Set TypingSpeed' with no value, or 'Wait@ s' where the '@' is followed by a unit instead of a number — anything where the token after the time-taking keyword is not a NUMBER.","commonSituations":"Omitting the numeric value ('Sleep' with no argument is handled elsewhere, but 'Sleep 5' vs 'Sleep s' confusion); unit-first ordering; assuming bare units like 'ms' alone are valid durations.","solutions":["Provide a number, with or without a unit: 'Sleep 5', 'Sleep 500ms', 'Set TypingSpeed 50ms'.","Ensure the number comes before the unit: 'Sleep 5s', not 'Sleep s5'.","Check the @ speed suffix syntax: 'Wait@5s', not 'Wait@s'."],"exampleFix":"// before (.tape)\nSleep ms\n// after (.tape)\nSleep 500ms","handlingStrategy":"validation","validationCode":"func hasNumericTime(args []string) bool { if len(args) == 0 { return false }; _, err := strconv.ParseFloat(args[0], 64); return err == nil }","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always put the number before the unit: 'Sleep 5s' not 'Sleep s5'","Never write a unit without a preceding number","Review tape diffs for accidentally deleted duration values"],"tags":["parser","duration","missing-argument"],"backgroundTag":"missing-required-argument","analyzedSha":"c073383b5de0b1f57bf514113029c306bc986539","analyzedAt":"2026-09-02T01:53:05.682Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-09T06:17:21.866Z"}