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

  1. Remove the bare '>' from the sequence or express it as the named key form so it is inside a proper key-event token.
  2. Ensure every '<' has a matching '>'; an unterminated group is the sibling failure.
  3. 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

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


AI-assisted analysis of helix-editor/helix@079a789e8c (2026-08-16). Data as JSON: /api/errors/3501041f05c9ad40. Report an issue: GitHub.