helix-editor/helix · error · anyhow::Error
{err}
Error message
{err} What it means
expand_inner recursively expands percent tokens inside a %( ... ) / expand-style token. When the inner tokenizer's parse_percent_token fails, the ParseArgsError is wrapped with anyhow!("{err}") — the most common inner error being "'%' was not properly escaped. Please use '%%'". So this error is a re-surface of a tokenizer failure for malformed percent syntax inside the expansion.
Source
Thrown at helix-view/src/expansion.rs:224
let mut start = 0;
while let Some(offset) = content[start..].find('%') {
let idx = start + offset;
if content.as_bytes().get(idx + '%'.len_utf8()).copied() == Some(b'%') {
// Treat two percents in a row as an escaped percent.
escaped.push_str(&content[start..=idx]);
// Skip over both percents.
start = idx + ('%'.len_utf8() * 2);
} else {
// Otherwise interpret the percent as an expansion. Push up to (but not
// including) the percent token.
escaped.push_str(&content[start..idx]);
// Then parse the expansion,
let mut tokenizer = Tokenizer::new(&content[idx..], true);
let token = tokenizer
.parse_percent_token()
.unwrap()
.map_err(|err| anyhow!("{err}"))?;
// expand it (this is the recursive part),
let expanded = expand(editor, token)?;
escaped.push_str(expanded.as_ref());
// and move forward to the end of the expansion.
start = idx + tokenizer.pos();
}
}
if escaped.is_empty() {
Ok(content)
} else {
escaped.push_str(&content[start..]);
Ok(Cow::Owned(escaped))
}
}
// Note: the lifetime of the expanded variable (the `Cow`) must not be tied to the lifetime of
// the borrow of `Editor`. That would prevent commands from mutating the `Editor` until theView on GitHub (pinned to 079a789e8c)
Solutions
- Escape every literal % as %% inside the expanded content.
- Check that all %{...} and %u{...} groups are closed with '}'.
- Re-run with the inner content simplified to isolate which percent token fails to parse.
Example fix
# before :echo %(echo "100% done") # after :echo %(echo "100%% done")
Defensive patterns
Strategy: try-catch
Validate before calling
use helix_core::command_line::Tokenizer;
// dry-run: every '%' inside content must parse or be doubled
fn percent_syntax_ok(content: &str) -> bool {
let mut t = Tokenizer::new(content, true);
loop {
match t.next() {
None | Some(Ok(_)) if t.pos() >= content.len() => return true,
Some(Err(_)) => return false,
_ => continue,
}
}
} Try / catch
let expanded = expand_inner(editor, content).map_err(|err| {
anyhow!("expansion of '{content}' failed: {err:#}") // inner err is terse; add the payload
})?; Prevention
- Double every literal % as %% inside expandable content.
- Close all %{...} / %u{...} groups.
- Dry-run the tokenizer over user-built strings before expanding them.
When it happens
Trigger: A single unescaped % inside an expand token's content, an unterminated %{...} or %u{...}, or any percent construct the tokenizer cannot parse at that position — all while command-line validation is enabled so the name token cannot be the culprit.
Common situations: Building format strings that contain literal percent signs (date formats, printf-style templates) without doubling them; nested expansions where an inner %u{...} is missing its closing brace.
Related errors
- unknown variable '{}'
- could not interpret '{}' as a Unicode character code
- 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/4c4b7a34ec4208a0.
Report an issue: GitHub.