zed-industries/zed · error
unexpected character '{next:?}'
Error message
unexpected character '{next:?}' What it means
KeyBindingContextPredicate::parse parses a complete context predicate expression and requires the entire source to be consumed. After parse_expr returns, any remaining character (whitespace is consumed by atoms and operators, so this is real syntax) means trailing text the grammar could not attach: typically an unbalanced ')' or a second predicate with no operator between them.
Source
Thrown at crates/gpui/src/keymap/context.rs:253
/// `StatusBar && mode == visible` -> A predicate that will match a context with the
/// identifier `StatusBar` and the key `mode`
/// with the value `visible`
///
///
/// There is also a special child `>` operator that will match a predicate that is
/// below another predicate:
///
/// `StatusBar > mode == visible` -> A predicate that will match a context identifier `StatusBar`
/// and a child context that has the key `mode` with the
/// value `visible`
///
/// This syntax supports `!=`, `||` and `&&` as logical operators.
/// You can also preface an operation or check with a `!` to negate it.
pub fn parse(source: &str) -> Result<Self> {
let source = skip_whitespace(source);
let (predicate, rest) = Self::parse_expr(source, 0)?;
if let Some(next) = rest.chars().next() {
anyhow::bail!("unexpected character '{next:?}'");
} else {
Ok(predicate)
}
}
/// Find the deepest depth at which the predicate matches.
pub fn depth_of(&self, contexts: &[KeyContext]) -> Option<usize> {
for depth in (0..=contexts.len()).rev() {
let context_slice = &contexts[0..depth];
if self.eval_inner(context_slice, contexts) {
return Some(depth);
}
}
None
}
/// Eval a predicate against a set of contexts, arranged from lowest to highest.
#[allow(unused)]View on GitHub (pinned to f4178619ac)
Solutions
- Delete or fix the trailing text: usually an extra ')' or a predicate missing its '&&'/'||'/'>' operator
- Rebalance parentheses across the whole expression
- Check the editor log: the parse error is reported against the exact context string, which pinpoints the binding
Example fix
// before (keymap.json) "context": "mode == visual)" // after "context": "mode == visual"
Defensive patterns
Strategy: validation
Validate before calling
// validate keymap context strings at load time
for binding in &keymap.bindings {
KeyBindingContextPredicate::parse(binding.context.as_str())
.with_context(|| format!("bad context in binding {binding:?}"))?;
} Type guard
fn is_parseable_context(source: &str) -> bool {
KeyBindingContextPredicate::parse(source).is_ok()
} Prevention
- Keep predicates small and deliberately parenthesized
- Lint user keymap files on save with the real parser
- Copy working examples from the default keymaps
When it happens
Trigger: Keymap context strings like 'mode == visual)' (stray close paren), 'normal (mode == visual)' (missing &&), or 'a b' (two identifiers not joined by an operator).
Common situations: Hand-edited keymap.json typos: an extra parenthesis left after editing; parentheses unbalanced across the expression; a fragment left behind after deleting an operator.
Related errors
- operands of == must be identifiers
- operands of != must be identifiers
- invalid RGBA hex color: '{value}'. Expected #rgb, #rgba, #rr
- Device lost: {err}
- database not initialized
AI-assisted analysis of zed-industries/zed@f4178619ac (2026-08-20).
Data as JSON: /api/errors/570a25603457a31b.
Report an issue: GitHub.