helix-editor/helix · error · anyhow::Error
unknown variable '{}'
Error message
unknown variable '{}' What it means
expand() handles command-line tokens like %{name}; it resolves the inner name through Variable::from_name, which only knows the built-in variables: cursor_line, cursor_column, buffer_name, file_path_absolute, line_ending, current_working_directory, workspace_directory, language, selection, selection_line_start, selection_line_end. An unknown name produces "unknown variable '{name}'".
Source
Thrown at helix-view/src/expansion.rs:112
"selection" => Some(Self::Selection),
"selection_line_start" => Some(Self::SelectionLineStart),
"selection_line_end" => Some(Self::SelectionLineEnd),
_ => None,
}
}
}
/// Expands the given command line token.
///
/// Note that the lifetime of the expanded variable is only bound to the input token and not the
/// `Editor`. See `expand_variable` below for more discussion of lifetimes.
pub fn expand<'a>(editor: &Editor, token: Token<'a>) -> Result<Cow<'a, str>> {
// Note: see the `TokenKind` documentation for more details on how each branch should expand.
match token.kind {
TokenKind::Unquoted | TokenKind::Quoted(_) => Ok(token.content),
TokenKind::Expansion(ExpansionKind::Variable) => {
let var = Variable::from_name(&token.content)
.ok_or_else(|| anyhow!("unknown variable '{}'", token.content))?;
expand_variable(editor, var)
}
TokenKind::Expansion(ExpansionKind::Unicode) => {
if let Some(ch) = u32::from_str_radix(token.content.as_ref(), 16)
.ok()
.and_then(char::from_u32)
{
Ok(Cow::Owned(ch.to_string()))
} else {
Err(anyhow!(
"could not interpret '{}' as a Unicode character code",
token.content
))
}
}
TokenKind::Expand => expand_inner(editor, token.content),
TokenKind::Expansion(ExpansionKind::Shell) => expand_shell(editor, token.content),View on GitHub (pinned to 079a789e8c)
Solutions
- Use one of the documented names: cursor_line, cursor_column, buffer_name, file_path_absolute, line_ending, current_working_directory, workspace_directory, language, selection, selection_line_start, selection_line_end.
- Check book/src/command-line.md (the Variable list lives there and in Variable::VARIANTS) for your Helix version.
- For shell/env data, use the shell expansion (%sh{...}) instead of an undefined variable.
Example fix
# before
:echo %{cursorline}
# after
:echo %{cursor_line} Defensive patterns
Strategy: validation
Validate before calling
let name = token_content; // text between %{ ... }
if Variable::from_name(name).is_none() {
return Err(anyhow!(
"unknown variable '{name}'; known: {:?}",
Variable::VARIANTS.iter().map(|v| v.as_str()).collect::<Vec<_>>()
));
}
expand(editor, token)?; Type guard
fn known_variable(s: &str) -> Option<Variable> {
Variable::from_name(s)
} Try / catch
match expand(editor, token) {
Ok(v) => { /* use v */ }
Err(err) if err.to_string().contains("unknown variable") => {
// degrade gracefully: keep the literal text instead of failing the command
Ok(Cow::Borrowed(token.content))
}
Err(err) => return Err(err),
} Prevention
- Validate variable names against Variable::VARIANTS before expansion.
- Provide completion for %{...} in command-line UIs (VARIANTS exists for this).
- Version-pin documentation; the variable list changes between Helix releases.
When it happens
Trigger: Typing :echo %{linenumber} or any %{...} whose content is not exactly one of the eleven built-in names; passing unvalidated user input as a variable expansion; using a name added in a newer Helix version against an older build (or vice versa).
Common situations: Typos and snake_case mistakes (cursorLine vs cursor_line); assuming arbitrary editor state (like %{register} or %{mode}) is available as a variable; feature-drift between Helix versions where the variable list changed.
Related errors
- could not interpret '{}' as a Unicode character code
- {err}
- Failed to parse snippet. Remaining input: {}
- Command not provided
- Incorrect transport {}
AI-assisted analysis of helix-editor/helix@079a789e8c (2026-08-16).
Data as JSON: /api/errors/b279b3cb69501569.
Report an issue: GitHub.