can1357/oh-my-pi · error · io::Error (ErrorKind::InvalidInput)
Unterminated quote: {q}
Error message
Unterminated quote: {q} What it means
pi-builtins' xargs implementation tokenizes its input (stdin or a file) with shell-like quoting: an opening `"` or `'` starts a quoted region whose matching close quote must appear before end-of-input. When the reader reaches EOF while still inside a quoted region, it raises this InvalidInput io::Error instead of silently producing a mangled argument, mirroring real xargs/shell behavior where an unclosed quote is a hard input error.
Source
Thrown at crates/pi-builtins/src/xargs.rs:538
}
let mut escape: Option<Escape> = None;
let mut i = 0;
loop {
if i == pending.len() {
pending.resize(4096, 0);
// Already hit the end of our buffer, so read in some more data.
let bytes_read = loop {
match self.rd.read(host, &mut pending[..]) {
Ok(bytes_read) => break bytes_read,
Err(e) if e.kind() == io::ErrorKind::Interrupted => continue,
Err(e) => return Err(e),
}
};
if bytes_read == 0 {
if let Some(Escape::Quote(q)) = &escape {
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
format!("Unterminated quote: {q}"),
));
}
if i == 0 {
return Ok(None);
}
pending.clear();
break;
}
pending.resize(bytes_read, 0);
i = 0;
}
match (&escape, pending[i]) {
(Some(Escape::Quote(quote)), c) if c == *quote => escape = None,
(Some(Escape::Quote(_)), c) => result.push(c),View on GitHub (pinned to 9690622007)
Solutions
- Fix the input so every quote character is balanced — check the last line of the piped/file input for an unmatched " or ' before it reaches xargs.
- Escape embedded quote characters with a backslash (e.g. it\'s.txt) instead of relying on raw quotes in the input stream.
- Ensure the upstream producer finished writing and closed the stream correctly; a truncated pipe (SIGPIPE, crash) leaves the quote unterminated.
- Pre-process the input to strip or requote problematic filenames, e.g. with `printf '%q\n'` or a null-delimited producer (`find -print0 | xargs -0`) when supported.
Example fix
// before: unbalanced quote in piped input echo "file1 'file2 | xargs pi-xargs // error: Unterminated quote: ' // after: balanced quotes / escaped embedded quote echo "file1 'file2'" | xargs pi-xargs // or: echo "it\\'s.txt" | xargs pi-xargs
Defensive patterns
Strategy: validation
Validate before calling
function validateXargsInput(input) {
let quote = null;
for (const ch of input) {
if (quote) { if (ch === quote) quote = null; }
else if (ch === '"' || ch === "'") quote = ch;
else if (ch === '\\') continue; // next char escaped
}
if (quote) throw new Error(`Input has unterminated ${quote} before xargs`);
} Try / catch
try {
await runXargs(input);
} catch (err) {
if (err instanceof Error && /Unterminated quote/.test(err.message)) {
console.error('Fix the unbalanced quote in the piped input and retry');
} else {
throw err;
}
} Prevention
- Generate xargs input from producers that emit one safely-quoted record per line (printf '%q\n') or NUL-delimited output (find -print0 | xargs -0).
- Never hand-write pipelines that embed filenames containing quotes or apostrophes without escaping.
- Check producer exit codes before piping so a truncated stream never reaches xargs.
When it happens
Trigger: Feeding xargs input via stdin or InputSource::Reader where a quote character (" or ') is opened and the input stream ends (bytes_read == 0) before the matching closing quote is read — e.g. a truncated pipe, a file cut off mid-line, or text pasted with a missing closing quote.
Common situations: Piping partially-written output from another process (producer killed mid-stream), copying a file list from an editor with an unbalanced quote, generated argument files with embedded apostrophes in filenames (e.g. "it's.txt") that weren't escaped, or a here-document/redirect that got truncated.
Related errors
- I/O error occurred
- {}
- Computed edit range is out of bounds
- unknown function: {0}
- unknown key binding function: {0}
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/1350900ee5b59b18.
Report an issue: GitHub.