charmbracelet/vhs · error · parser.Error

Not a valid modifier

Error message

Not a valid modifier

What it means

When parsing a Ctrl command's arguments, vhs only accepts modifiers and single keys: keyword keys (Enter, Space, Tab, arrows like Up/Down), or single-character strings. Any other token — notably a string longer than one character that is not a known key — produces this error, immediately followed by 'Invalid control argument: <literal>'.

Source

Thrown at parser/parser.go:339

		case peek.Type == token.ENTER,
			peek.Type == token.SPACE,
			peek.Type == token.BACKSPACE,
			peek.Type == token.MINUS,
			peek.Type == token.AT,
			peek.Type == token.LEFT_BRACKET,
			peek.Type == token.RIGHT_BRACKET,
			peek.Type == token.CARET,
			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>

View on GitHub (pinned to c073383b5d)

Solutions

  1. Use valid key names from vhs's keys list (Enter, Space, Tab, Backspace, Delete, Up, Down, Left, Right, PageUp, PageDown, etc.) with correct capitalization.
  2. Use single characters for character keys: 'Ctrl+a', not 'Ctrl+a!' typos.
  3. Check token.Keywords / keys.go for the exact supported key set.

Example fix

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

Strategy: validation

Validate before calling

func validCtrlArg(a string) bool { if len(a) == 1 { return true }; _, ok := token.Keywords[a]; return ok }

Type guard

func isKnownKey(s string) bool { k, ok := token.Keywords[s]; return ok && k != token.ILLEGAL }

Prevention

When it happens

Trigger: A Ctrl argument is a multi-character string that is not a registered key keyword, e.g. 'Ctrl+abc', 'Ctrl+enter' (lowercase, not a keyword), or 'Ctrl+F12' if that key is not in token.Keywords.

Common situations: Lowercasing key names ('ctrl+enter' when keywords are case-sensitive); trying key names vhs does not define; pasting combos from other tools' syntax.

Related errors


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