{"record":{"id":"09623e6db9419122","repo":"can1357/oh-my-pi","slug":"overlapping-replacements-detected-refine-pattern","errorCode":null,"errorMessage":"Overlapping replacements detected; refine pattern to avoid ambiguous edits","messagePattern":"Overlapping replacements detected; refine pattern to avoid ambiguous edits","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"critical","filePath":"crates/pi-ast/src/ops.rs","lineNumber":316,"sourceCode":"\tlet mut sorted: Vec<&Edit<String>> = edits.iter().collect();\n\tsorted.sort_by(|a, b| {\n\t\ta.position\n\t\t\t.cmp(&b.position)\n\t\t\t.then(a.deleted_length.cmp(&b.deleted_length))\n\t\t\t.then(a.inserted_text.cmp(&b.inserted_text))\n\t});\n\t// Byte-identical edits (same span, same replacement) are one deterministic\n\t// edit: multiple patterns matching the same node collapse instead of\n\t// tripping the overlap check. Only divergent overlaps are ambiguous.\n\tsorted.dedup_by(|a, b| {\n\t\ta.position == b.position\n\t\t\t&& a.deleted_length == b.deleted_length\n\t\t\t&& a.inserted_text == b.inserted_text\n\t});\n\tlet mut prev_end = 0usize;\n\tfor edit in &sorted {\n\t\tif edit.position < prev_end {\n\t\t\treturn Err(anyhow!(\n\t\t\t\t\"Overlapping replacements detected; refine pattern to avoid ambiguous edits\"\n\t\t\t));\n\t\t}\n\t\tprev_end = edit.position.saturating_add(edit.deleted_length);\n\t}\n\n\tlet mut output = content.to_string();\n\tfor edit in sorted.into_iter().rev() {\n\t\tlet start = edit.position;\n\t\tlet end = edit.position.saturating_add(edit.deleted_length);\n\t\tif end > output.len() || start > end {\n\t\t\treturn Err(anyhow!(\"Computed edit range is out of bounds\"));\n\t\t}\n\t\tlet replacement = std::str::from_utf8(&edit.inserted_text)\n\t\t\t.map_err(|err| anyhow!(\"Replacement text is not valid UTF-8: {err}\"))?;\n\t\toutput.replace_range(start..end, replacement);\n\t}\n\tOk(output)","sourceCodeStart":298,"sourceCodeEnd":334,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/crates/pi-ast/src/ops.rs#L298-L334","documentation":"_write_json checks process.stdin before writing a command frame. If the subprocess's stdin pipe is None (process exited and pipes closed, or spawned without stdin=PIPE), it raises RpcProcessExitError, signalling the RPC server side is gone or misconfigured.","triggerScenarios":"The child RPC process has crashed/exited and its stdin pipe was closed; the subprocess was created without stdin redirection; a concurrent stop() closed stdin while a write was in flight.","commonSituations":"Server binary crashed earlier (or never started correctly), OS killed the child, or long-running session where the server died mid-session and the next command is attempted.","solutions":["Restart the client: stop() the current instance, create a new RpcClient, start() again, and retry the command.","Check server stderr/logs to find why the child process exited before the write.","Verify the spawn command and that the client constructs the process with stdin=PIPE.","Catch RpcProcessExitError around requests and implement a restart-and-retry wrapper."],"exampleFix":"// before\nresult = await client.request(\"prompt\", {...})  # raises after server died\n// after\ntry:\n    result = await client.request(\"prompt\", {...})\nexcept RpcProcessExitError:\n    await client.stop()\n    client = RpcClient(cmd)\n    await client.start()\n    result = await client.request(\"prompt\", {...})","handlingStrategy":"try-catch","validationCode":"if client._process is None or client._process.poll() is not None:\n    await client.stop(); await client.start()  # restart before sending","typeGuard":"def process_alive(client: RpcClient) -> bool:\n    p = client._process\n    return p is not None and p.poll() is None","tryCatchPattern":"try:\n    result = await client.request(\"prompt\", payload)\nexcept RpcProcessExitError:\n    await client.stop()\n    client = RpcClient(cmd)\n    await client.start()\n    result = await client.request(\"prompt\", payload)","preventionTips":["Monitor the child process (poll()) and restart proactively when it dies.","Capture server stderr and exit code to diagnose recurring crashes.","Always spawn through the library's start() so stdin=PIPE is set up correctly."],"tags":["process","rpc","stdin","crash"],"backgroundTag":"broken-pipe","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}