helix-editor/helix · error · anyhow::Error
Unmatched '>'
Error message
Unmatched '>'
What it means
When converting a plain string into a sequence of key events, characters are consumed one at a time and '<' starts a named-key region ending at '>'. A '>' encountered in the plain-character position (not closing a '<...>' group) sets the error "Unmatched '>'"; similarly a '<' with no closing '>' fails with "'>' expected". A bare '-' in the plain position is translated to the special key name minus.
Source
Thrown at helix-view/src/input.rs:699
let mut keys_res: anyhow::Result<_> = Ok(Vec::new());
let mut i = 0;
while let Ok(keys) = &mut keys_res {
if i >= keys_str.len() {
break;
}
if !keys_str.is_char_boundary(i) {
i += 1;
continue;
}
let s = &keys_str[i..];
let mut end_i = 1;
while !s.is_char_boundary(end_i) {
end_i += 1;
}
let c = &s[..end_i];
if c == ">" {
keys_res = Err(anyhow!("Unmatched '>'"));
} else if c != "<" {
keys.push(if c == "-" { keys::MINUS } else { c });
i += end_i;
} else {
match s.find('>').context("'>' expected") {
Ok(end_i) => {
keys.push(&s[1..end_i]);
i += end_i + 1;
}
Err(err) => keys_res = Err(err),
}
}
}
keys_res.and_then(|keys| keys.into_iter().map(str::parse).collect())
}
#[cfg(test)]
mod test {View on GitHub (pinned to 079a789e8c)
Solutions
- Remove the bare '>' from the sequence or express it as the named key form so it is inside a proper key-event token.
- Ensure every '<' has a matching '>'; an unterminated group is the sibling failure.
- A standalone '-' is remapped to minus automatically; '>' has no such plain form, so never emit it as a raw character.
Example fix
// before
parse_keys("a>b") // Err: Unmatched '>'
// after
parse_keys("a<b>") // '>' only as part of a named key group Defensive patterns
Strategy: validation
Validate before calling
fn key_sequence_syntax_ok(s: &str) -> bool {
let mut rest = s;
while let Some(i) = rest.find(['<', '>']) {
if rest.as_bytes()[i] == b'>' { return false; } // bare '>' in plain position
match rest[i..].find('>') {
Some(end) => rest = &rest[i + end + 1..],
None => return false, // unterminated '<'
}
}
true
} Type guard
fn parse_key_sequence(s: &str) -> Option<Vec<KeyEvent>> {
helix_view::input::str_to_sequence(s).ok()
} Try / catch
let keys = parse_sequence(keys_str)
.map_err(|e| anyhow!("invalid key sequence '{keys_str}': {e:#}"))?; Prevention
- Never place a bare '>' in a key sequence; it only closes a '<...>' group.
- Dry-run-parse any user- or macro-supplied sequence before feeding it.
- A standalone '-' is auto-translated to minus; '>' has no raw form.
When it happens
Trigger: Passing a sequence containing a literal '>' outside angle brackets, e.g. ">" or "a>b"; sequences where a '<' group is opened but the intended '>' was dropped, leaving a later stray one; feeding user-supplied strings as key sequences.
Common situations: Macros or :feed-style sequences that include > in the payload; converting human text like "a=>b" into keys; '>' is special enough that configs generally express it via the named key gt inside angle brackets.
Related errors
- Missing key code
- Invalid function key '{}'
- Key '-' cannot be used with modifiers, use '{}' instead
- Invalid key code '{}'
- Invalid key modifier '{}-'
AI-assisted analysis of helix-editor/helix@079a789e8c (2026-08-16).
Data as JSON: /api/errors/3501041f05c9ad40.
Report an issue: GitHub.