wezterm/wezterm · error

invalid \\M escape: {}

Error message

invalid \\M escape: {}

What it means

Same tmux control-mode unvis decoder: after the two-byte intro \M the parser only accepts '-' (meta prefix, next byte OR 0o200) or '^' (meta-ctrl). Any other byte after \M raises this error because the meta escape cannot be completed.

Source

Thrown at wezterm-escape-parser/src/tmux_cc/mod.rs:622

                    }
                    b'$' => {
                        // Hidden marker
                        *state = State::Ground;
                    }
                    _ => {
                        // Invalid syntax
                        bail!("Invalid \\ escape: {}", b);
                    }
                }
            }

            State::Meta => {
                if b == b'-' {
                    *state = State::Meta1;
                } else if b == b'^' {
                    *state = State::Ctrl(0o200);
                } else {
                    bail!("invalid \\M escape: {}", b);
                }
            }

            State::Meta1 => {
                result.push(b | 0o200);
                *state = State::Ground;
            }

            State::Ctrl(c) => {
                if b == b'?' {
                    result.push(*c | 0o177);
                } else {
                    result.push((b & 0o37) | *c);
                }
                *state = State::Ground;
            }

            State::Octal2(prior) => {

View on GitHub (pinned to 3ff7522b96)

Solutions

  1. Fix the producer to emit the complete form \M-<byte> or \M^<byte>
  2. Upgrade wezterm if the tmux version in use added a new \M form
  3. When consuming untrusted/replayed streams, catch the error and skip the malformed line

Example fix

// before: emitting an incomplete meta escape
output.push_str("\\Ma");

// after: meta-a is \M-a (sets bit 0o200 on the following byte)
output.push_str("\\M-a");
Defensive patterns

Strategy: try-catch

Validate before calling

// \M must be followed by '-' or '^', then one more byte
fn meta_escapes_ok(bytes: &[u8]) -> bool {
    let mut i = 0;
    while i + 1 < bytes.len() {
        if bytes[i] == b'\\' && bytes[i + 1] == b'M' {
            match bytes.get(i + 2) {
                Some(b'-') | Some(b'^') => i += 3, // \M- or \M^ plus payload byte
                None => return true,              // may continue in next chunk
                _ => return false,
            }
        } else {
            i += 1;
        }
    }
    true
}

Try / catch

match parser.advance_bytes(&chunk) {
    Ok(events) => events_out.extend(events),
    Err(err) if err.to_string().contains("\\M escape") => {
        log::warn!("skipping line with bad \\M escape: {err:#}");
    }
    Err(err) => return Err(err),
}

Prevention

When it happens

Trigger: A parsed line containing \M followed by something other than '-' or '^' (e.g. \Mx), or a lone \M where the next byte is a newline or unrelated character; typically from a producer using a different meta-escape dialect or from unescaped binary in the stream.

Common situations: Hand-crafted or replayed tmux control-mode test transcripts that use \M incorrectly; tmux/wezterm version drift changing the accepted meta forms.

Related errors


AI-assisted analysis of wezterm/wezterm@3ff7522b96 (2026-08-20). Data as JSON: /api/errors/e81c3628b3e688e3. Report an issue: GitHub.