charmbracelet/vhs · error · parser.Error

${literal} expects string

Error message

${literal} expects string

What it means

Fires in parseType when the token after the Type command (and optional speed option) is not a STRING, i.e. no text to type was given. The loop that concatenates string tokens never runs, so the Type command ends up with an empty Args.

Source

Thrown at parser/parser.go:587

// parseShow parses a Show command.
//
//	Show
func (p *Parser) parseShow() Command {
	cmd := Command{Type: token.SHOW}
	return cmd
}

// parseType parses a type command.
// A type command takes a string to type.
//
//	Type "string"
func (p *Parser) parseType() Command {
	cmd := Command{Type: token.TYPE}

	cmd.Options = p.parseSpeed()

	if p.peek.Type != token.STRING {
		p.errors = append(p.errors, NewError(p.peek, p.cur.Literal+" expects string"))
	}

	for p.peek.Type == token.STRING {
		p.nextToken()
		cmd.Args += p.cur.Literal

		// If the next token is a string, add a space between them.
		// Since tokens must be separated by a whitespace, this is most likely
		// what the user intended.
		//
		// Although it is possible that there may be multiple spaces / tabs between
		// the tokens, however if the user was intending to type multiple spaces
		// they would need to use a string literal.

		if p.peek.Type == token.STRING {
			cmd.Args += " "
		}
	}

View on GitHub (pinned to c073383b5d)

Solutions

  1. Quote the text to type, e.g. Type "hello world"
  2. Move the typing speed option so it does not swallow the string, e.g. Type@100ms "text"
  3. Remove the Type command if nothing should be typed
Defensive patterns

Strategy: validation

When it happens

Trigger: Thrown at parser/parser.go:587 when the library encounters an invalid state.

Common situations: See trigger scenarios.


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