zed-industries/zed · error

operands of != must be identifiers

Error message

operands of != must be identifiers

What it means

new_neq mirrors new_eq: '!=' is only defined between two Identifier predicates because evaluation resolves the left identifier's value and compares it for inequality. A composite operand (group, &&/|| chain, '>' descendant, negation) on either side makes the constructor bail.

Source

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

    }

    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 {
        let mut first = true;
        self.fmt_joined_inner(f, separator, operator, needs_parens, &mut first)
    }

    fn fmt_joined_inner(
        &self,
        f: &mut fmt::Formatter<'_>,
        separator: &str,

View on GitHub (pinned to f4178619ac)

Solutions

  1. Keep both sides of != as bare identifiers: mode != normal
  2. Express multi-value exclusion as a chain: (mode != normal && mode != visual)
  3. Place negation outside the whole comparison if needed: !(mode == normal)

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 'mode != (a || b)', '(a && b) != c', or '!a != b' — any != whose operands are not both identifiers.

Common situations: Negated comparisons against multiple values; mixing precedence parentheses into inequality expressions; porting boolean shapes from other tools.

Related errors


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