rust-lang/rust-analyzer · error · SsrError

Unexpected token while parsing placeholder: '{}'

Error message

Unexpected token while parsing placeholder: '{}'

What it means

When parsing an SSR placeholder, the parser accepts either $name or ${name:constraints}. Inside the ${...} form, only ':' (starting a constraint) or '}' (ending the placeholder) may follow the name; any other token triggers this error naming the offending token text. It is raised inside parse_placeholder, invoked from parse_pattern.

Source

Thrown at crates/ide-ssr/src/parsing.rs:292

            SyntaxKind::IDENT => {
                name = Some(token.text);
            }
            T!['{'] => {
                let token =
                    tokens.next().ok_or_else(|| SsrError::new("Unexpected end of placeholder"))?;
                if token.kind == SyntaxKind::IDENT {
                    name = Some(token.text);
                }
                loop {
                    let token = tokens
                        .next()
                        .ok_or_else(|| SsrError::new("Placeholder is missing closing brace '}'"))?;
                    match token.kind {
                        T![:] => {
                            constraints.push(parse_constraint(tokens)?);
                        }
                        T!['}'] => break,
                        _ => bail!("Unexpected token while parsing placeholder: '{}'", token.text),
                    }
                }
            }
            _ => {
                bail!("Placeholders should either be $name or ${{name:constraints}}");
            }
        }
    }
    let name = name.ok_or_else(|| SsrError::new("Placeholder ($) with no name"))?;
    Ok(Placeholder::new(name, constraints))
}

fn parse_constraint(tokens: &mut std::vec::IntoIter<Token>) -> Result<Constraint, SsrError> {
    let constraint_type = tokens
        .next()
        .ok_or_else(|| SsrError::new("Found end of placeholder while looking for a constraint"))?
        .text
        .to_string();

View on GitHub (pinned to e8f7e90aa3)

Solutions

  1. Use ':' before constraints: ${name:kind(literal)}
  2. Close the placeholder with '}' if no constraints are needed: ${name} — or just use $name
  3. Check the token text in the message and remove/replace the unexpected character

Example fix

// before
let pattern = "foo(${x = kind(literal)})";
// after
let pattern = "foo(${x:kind(literal)})";
Defensive patterns

Strategy: validation

Validate before calling

fn valid_placeholder_syntax(p: &str) -> bool {
    let mut chars = p.char_indices();
    while let Some((i, c)) = chars.next() {
        if c == '$' {
            match chars.next() {
                Some((_, n)) if n.is_alphabetic() || n == '_' => {}
                Some((_, '{')) => {
                    let rest = &p[i + 2..];
                    if let Some(end) = rest.find('}') {
                        let inner = &rest[..end];
                        if !inner.contains('=') && !inner.contains(' ') { continue; }
                        return false; // only ':' constraints allowed inside
                    }
                    return false; // unterminated ${
                }
                _ => return false,
            }
        }
    }
    true
}

Try / catch

match parse_pattern(pattern) {
    Ok(p) => use_pattern(p),
    Err(e) if e.message.contains("Unexpected token while parsing placeholder") => {
        eprintln!("Bad placeholder: use $name or ${{name:constraints}}");
    }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: Writing an SSR pattern with ${name followed by a token that is neither ':' nor '}', e.g. '${name kind}' or '${name=Constraint}', or inserting whitespace-adjacent invalid syntax inside the braces.

Common situations: Hand-writing constraint syntax from memory and getting the separator wrong; mixing shell/mustache ${var} habits with SSR's constrained grammar; IDE search box typos.

Understand the failure class

Related errors


AI-assisted analysis of rust-lang/rust-analyzer@e8f7e90aa3 (2026-09-03). Data as JSON: /api/errors/40c8e18070121aa8. Report an issue: GitHub.