can1357/oh-my-pi · critical · anyhow::Error
Computed edit range is out of bounds
Error message
Computed edit range is out of bounds
What it means
Writing the JSON command frame to the subprocess stdin raised BrokenPipeError or OSError. The client converts it to RpcProcessExitError with the underlying OS message, since a failed stdin write almost always means the child process has exited.
Source
Thrown at crates/pi-ast/src/ops.rs:328
&& a.deleted_length == b.deleted_length
&& a.inserted_text == b.inserted_text
});
let mut prev_end = 0usize;
for edit in &sorted {
if edit.position < prev_end {
return Err(anyhow!(
"Overlapping replacements detected; refine pattern to avoid ambiguous edits"
));
}
prev_end = edit.position.saturating_add(edit.deleted_length);
}
let mut output = content.to_string();
for edit in sorted.into_iter().rev() {
let start = edit.position;
let end = edit.position.saturating_add(edit.deleted_length);
if end > output.len() || start > end {
return Err(anyhow!("Computed edit range is out of bounds"));
}
let replacement = std::str::from_utf8(&edit.inserted_text)
.map_err(|err| anyhow!("Replacement text is not valid UTF-8: {err}"))?;
output.replace_range(start..end, replacement);
}
Ok(output)
}
pub fn collect_matched_files(
cwd: &Path,
patterns: &[String],
) -> Result<Vec<MatchedFile>, std::io::Error> {
let globset = build_globset(patterns)?;
let mut builder = WalkBuilder::new(cwd);
builder
.hidden(false)
.git_ignore(true)
.git_global(true)View on GitHub (pinned to 9690622007)
Solutions
- Restart the RPC client (stop, new instance, start) and retry; the old process cannot be written to.
- Inspect server stderr and exit code to diagnose the original crash.
- Serialize stop() and request calls (locks/async ordering) so stop() doesn't close stdin mid-write.
- Wrap all requests in try/except RpcProcessExitError with automatic restart logic.
Example fix
// before
await client.stop()
await client.request("prompt", {...}) # broken pipe: stdin already closed
// after
await client.stop()
await client.start()
await client.request("prompt", {...}) Defensive patterns
Strategy: try-catch
Validate before calling
proc = client._process
if proc is None or proc.poll() is not None:
raise RuntimeError("RPC server not running; restart before sending commands") Type guard
def can_write(client: RpcClient) -> bool:
p = client._process
return p is not None and p.stdin is not None and p.poll() is None Try / catch
try:
result = await client.request("prompt", payload)
except RpcProcessExitError as exc:
logger.error("RPC write failed, restarting client", exc_info=exc)
await client.stop()
await client.start()
result = await client.request("prompt", payload) Prevention
- Add automatic restart-and-retry (with backoff) around all RPC requests.
- Never call stop() concurrently with in-flight requests; await pending requests first.
- Persist server stderr to logs so broken-pipe crashes are diagnosable.
When it happens
Trigger: Child RPC process died before/during the write; pipe buffer closed concurrently by stop(); EPIPE/EIO from the OS when the read end of the pipe is closed.
Common situations: Server crash from an unhandled exception, out-of-memory kill, or the server exiting after an earlier protocol error — the next request write then fails with a broken pipe.
Related errors
- Overlapping replacements detected; refine pattern to avoid a
- error writing 'standard output': {err}
- failed to write to stdout: {}
- Unsupported language '{value}'. Supported: {}
- Unable to infer language from file extension: {}. Specify `l
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/722602b11e6e918d.
Report an issue: GitHub.