zed-industries/zed · error

operands of == must be identifiers

Error message

operands of == must be identifiers

What it means

new_eq builds an Equal predicate only when both operands parsed as plain Identifiers — the only shape evaluation supports (it resolves the left identifier's value and compares it to the right). If either side is a composite (parenthesized group, &&/|| chain, '>' descendant, or negation), the constructor bails. Comparisons in this DSL are always identifier-to-identifier.

Source

Thrown at crates/gpui/src/keymap/context.rs:435

    }

    fn new_or(self, other: Self) -> Result<Self> {
        Ok(Self::Or(Box::new(self), Box::new(other)))
    }

    fn new_and(self, other: Self) -> Result<Self> {
        Ok(Self::And(Box::new(self), Box::new(other)))
    }

    fn new_child(self, other: Self) -> Result<Self> {
        Ok(Self::Descendant(Box::new(self), Box::new(other)))
    }

    fn new_eq(self, other: Self) -> Result<Self> {
        if let (Self::Identifier(left), Self::Identifier(right)) = (self, other) {
            Ok(Self::Equal(left, right))
        } else {
            anyhow::bail!("operands of == must be identifiers");
        }
    }

    fn new_neq(self, other: Self) -> Result<Self> {
        if let (Self::Identifier(left), Self::Identifier(right)) = (self, other) {
            Ok(Self::NotEqual(left, right))
        } else {
            anyhow::bail!("operands of != must be identifiers");
        }
    }

    fn fmt_joined(
        &self,
        f: &mut fmt::Formatter<'_>,
        separator: &str,
        operator: LogicalOperator,
        needs_parens: impl Fn(&Self) -> bool + Copy,
    ) -> fmt::Result {

View on GitHub (pinned to f4178619ac)

Solutions

  1. Keep both sides of == as bare identifiers: mode == visual
  2. Expand alternatives into a chain: (mode == normal || mode == visual) instead of mode == (normal || visual)
  3. Move composite logic outside the comparison and combine results with && or ||

Example fix

// before (keymap.json)
"context": "mode == (normal || visual)"

// after
"context": "(mode == normal || mode == visual)"
Defensive patterns

Strategy: validation

Validate before calling

KeyBindingContextPredicate::parse("(mode == normal || mode == visual)")?; // validate before install

Type guard

fn is_parseable_context(source: &str) -> bool {
    KeyBindingContextPredicate::parse(source).is_ok()
}

Prevention

When it happens

Trigger: Writing '(a && b) == c', 'mode == (normal || visual)', or 'A > B == c' — any == whose left or right operand is not a single identifier.

Common situations: Trying to compare against a group of alternatives on the right-hand side; adding precedence parentheses around one side of a comparison; copying boolean expression shapes from other keymap formats.

Related errors


AI-assisted analysis of zed-industries/zed@f4178619ac (2026-08-20). Data as JSON: /api/errors/c9917f4d2f78e888. Report an issue: GitHub.