{"record":{"id":"3501041f05c9ad40","repo":"helix-editor/helix","slug":"unmatched","errorCode":null,"errorMessage":"Unmatched '>'","messagePattern":"Unmatched '>'","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"helix-view/src/input.rs","lineNumber":699,"sourceCode":"    let mut keys_res: anyhow::Result<_> = Ok(Vec::new());\n    let mut i = 0;\n    while let Ok(keys) = &mut keys_res {\n        if i >= keys_str.len() {\n            break;\n        }\n        if !keys_str.is_char_boundary(i) {\n            i += 1;\n            continue;\n        }\n\n        let s = &keys_str[i..];\n        let mut end_i = 1;\n        while !s.is_char_boundary(end_i) {\n            end_i += 1;\n        }\n        let c = &s[..end_i];\n        if c == \">\" {\n            keys_res = Err(anyhow!(\"Unmatched '>'\"));\n        } else if c != \"<\" {\n            keys.push(if c == \"-\" { keys::MINUS } else { c });\n            i += end_i;\n        } else {\n            match s.find('>').context(\"'>' expected\") {\n                Ok(end_i) => {\n                    keys.push(&s[1..end_i]);\n                    i += end_i + 1;\n                }\n                Err(err) => keys_res = Err(err),\n            }\n        }\n    }\n    keys_res.and_then(|keys| keys.into_iter().map(str::parse).collect())\n}\n\n#[cfg(test)]\nmod test {","sourceCodeStart":681,"sourceCodeEnd":717,"githubUrl":"https://github.com/helix-editor/helix/blob/079a789e8cb08ead67f19e1971a1b7438b37354b/helix-view/src/input.rs#L681-L717","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\n parse_keys(\"a>b\") // Err: Unmatched '>'\n\n// after\n parse_keys(\"a<b>\") // '>' only as part of a named key group","handlingStrategy":"validation","validationCode":"fn key_sequence_syntax_ok(s: &str) -> bool {\n    let mut rest = s;\n    while let Some(i) = rest.find(['<', '>']) {\n        if rest.as_bytes()[i] == b'>' { return false; } // bare '>' in plain position\n        match rest[i..].find('>') {\n            Some(end) => rest = &rest[i + end + 1..],\n            None => return false, // unterminated '<'\n        }\n    }\n    true\n}","typeGuard":"fn parse_key_sequence(s: &str) -> Option<Vec<KeyEvent>> {\n    helix_view::input::str_to_sequence(s).ok()\n}","tryCatchPattern":"let keys = parse_sequence(keys_str)\n    .map_err(|e| anyhow!(\"invalid key sequence '{keys_str}': {e:#}\"))?;","preventionTips":["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."],"tags":["keymap","input","parsing","helix","rust"],"backgroundTag":null,"analyzedSha":"079a789e8cb08ead67f19e1971a1b7438b37354b","analyzedAt":"2026-08-16T09:09:59.668Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}