Released in v2026.10.17, 21 Oct 2026 Upstream bug fix in mise
mise: unterminated string in an aqua template hung the lexer
Our scanner flagged that mise, the polyglot tool-version manager, could loop forever while reading a template from an aqua registry entry. We sent a pull request with a fix and a regression test. Maintainer Jeff Dickey added more tests and merged it the same day, and the fix shipped in mise v2026.10.17.
What was wrong
mise installs many tools through the aqua backend. Registry entries describe download URLs and asset names with small Go-style templates, which mise parses with its own lexer in crates/aqua-registry/src/template.rs.
When a token started with ", lex() looked for the closing quote. If there was none, or every candidate quote was escaped, nothing advanced the input, and the outer while !code.is_empty() loop kept re-entering the same branch. A malformed template such as {{"abc}} made mise hang instead of reporting an error.
The escape check had a second problem: it took a byte offset from match_indices and used it as a character index (code.chars().nth(end)), and it did not tell \" apart from \\".
The templates come from the baked-in registry or from registries the user configures, so this is a hang on a broken registry entry, not a remote attack. Upstream describes it the same way.
How it was fixed
The lexer now counts the backslashes in front of each candidate quote byte-wise, treats the quote as closing only when that count is even, and stops with an error when no closing quote is found:
for (end, _) in code[1..].match_indices('"') {
- if code.chars().nth(end) != Some('\\') {
- tokens.push(Token::String(&code[1..end + 1]));
+ let slash_count = code[1..=end]
+ .bytes()
+ .rev()
+ .take_while(|&b| b == b'\\')
+ .count();
+ if slash_count % 2 == 0 {
+ tokens.push(Token::String(&code[1..=end]));
code = &code[end + 2..];
+ found = true;
break;
}
}
+ if !found {
+ bail!("unterminated string literal");
+ }
Our pull request added the regression test test_render_unterminated_string_fails. Jeff Dickey (jdx) then added commits that assert the exact “unterminated string literal” error and cover an empty string literal, and merged the pull request. The mise release notes for v2026.10.17 describe the change as:
“An aqua registry template with an unterminated string literal now fails with an "unterminated string literal" error instead of hanging. Escaped quotes after backslashes are also detected correctly.”
Timeline
- October 2026. A scan of the mise tree flagged the endless loop in
lex(); triage (model qwen3.8-27b) rated it valid, and a small input reproduced the hang. - 20 Oct. We opened pull request #14752 with the fix and a regression test. Jeff Dickey added three commits with formatting and extra tests and merged it the same day as b915325.
- 21 Oct. The fix shipped in mise v2026.10.17.
How to read this
The scanner’s triage pictured a malicious registry or a man in the middle. In practice the templates come from the registry mise ships with or from registries the user chose, so the honest description is a hang on a broken template. That is how upstream describes it, too. Findings are leads for people, not verdicts.