charmbracelet/vhs · error · parser.Error

Expected time after ${literal}

Error message

Expected time after ${literal}

What it means

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.

Source

Thrown at parser/parser.go:273

		count := p.peek.Literal
		p.nextToken()
		return count
	}

	return "1"
}

// parseTime parses a time argument.
//
//	<number>[ms]
func (p *Parser) parseTime() string {
	var t string

	if p.peek.Type == token.NUMBER {
		t = p.peek.Literal
		p.nextToken()
	} else {
		p.errors = append(p.errors, NewError(p.cur, "Expected time after "+p.cur.Literal))
		return ""
	}

	// Allow TypingSpeed to have bare units (e.g. 50ms, 100ms)
	if p.peek.Type == token.MILLISECONDS || p.peek.Type == token.SECONDS || p.peek.Type == token.MINUTES {
		t += p.peek.Literal
		p.nextToken()
	} else {
		t += "s"
	}

	return t
}

// parseCtrl parses a control command.
// A control command takes one or multiples characters and/or modifiers to type while ctrl is held down.
//
//	Ctrl[+Alt][+Shift]+<char>

View on GitHub (pinned to c073383b5d)

Solutions

  1. Provide a number, with or without a unit: 'Sleep 5', 'Sleep 500ms', 'Set TypingSpeed 50ms'.
  2. Ensure the number comes before the unit: 'Sleep 5s', not 'Sleep s5'.
  3. Check the @ speed suffix syntax: 'Wait@5s', not 'Wait@s'.

Example fix

// before (.tape)
Sleep ms
// after (.tape)
Sleep 500ms
Defensive patterns

Strategy: validation

Validate before calling

func hasNumericTime(args []string) bool { if len(args) == 0 { return false }; _, err := strconv.ParseFloat(args[0], 64); return err == nil }

Prevention

When it happens

Trigger: '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.

Common situations: 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.

Understand the failure class

Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.

Related errors


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