charmbracelet/vhs · error · parser.Error

Expected control character with args, got ${literal}

Error message

Expected control character with args, got ${literal}

What it means

A Ctrl command requires at least one argument (the key to press). If parseCtrl finishes consuming tokens and args is empty, it records 'Expected control character with args, got <literal>' naming the token that started the command. Note the parser still returns a CTRL command with empty Args, so this often accompanies downstream handling of an empty control sequence.

Source

Thrown at parser/parser.go:347

			peek.Type == token.BACKSLASH,
			peek.Type == token.LEFT,
			peek.Type == token.RIGHT,
			peek.Type == token.UP,
			peek.Type == token.DOWN,
			peek.Type == token.STRING && len(peek.Literal) == 1:
			args = append(args, peek.Literal)
		default:
			// Key arguments with len > 1 are not valid
			p.errors = append(p.errors,
				NewError(p.cur, "Not a valid modifier"),
				NewError(p.cur, "Invalid control argument: "+p.cur.Literal))
		}

		p.nextToken()
	}

	if len(args) == 0 {
		p.errors = append(p.errors, NewError(p.cur, "Expected control character with args, got "+p.cur.Literal))
	}

	ctrlArgs := strings.Join(args, " ")
	return Command{Type: token.CTRL, Args: ctrlArgs}
}

// parseAlt parses an alt command.
// An alt command takes a character to type while the modifier is held down.
//
//	Alt+<character>
func (p *Parser) parseAlt() Command {
	if p.peek.Type == token.PLUS {
		p.nextToken()
		if p.peek.Type == token.STRING ||
			p.peek.Type == token.ENTER ||
			p.peek.Type == token.LEFT_BRACKET ||
			p.peek.Type == token.RIGHT_BRACKET ||
			p.peek.Type == token.TAB {

View on GitHub (pinned to c073383b5d)

Solutions

  1. Add a key argument after Ctrl, e.g. 'Ctrl+c' or 'Ctrl+Enter'.
  2. Remove the Ctrl line entirely if no keypress was intended.

Example fix

// before (.tape)
Ctrl+
// after (.tape)
Ctrl+c
Defensive patterns

Strategy: validation

Validate before calling

func ctrlHasKey(args []string) bool { return len(args) > 0 }

Prevention

When it happens

Trigger: 'Ctrl' appears with no following key token, e.g. a lone 'Ctrl' line, or 'Ctrl+' where '+' is followed by nothing valid so no argument is ever appended.

Common situations: Incomplete editing of a tape line (deleting the key but leaving Ctrl); truncation of the tape file; copying 'Ctrl+' from a key-combo template without filling in the key.

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/2cec54849ecd8ea6. Report an issue: GitHub.