aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorOgulcan Celik <ogulcancelik@gmail.com>2026-07-07 15:21:40 +0300
committerOgulcan Celik <ogulcancelik@gmail.com>2026-07-07 15:35:20 +0300
commitb017d212905b4de04770100ad993cb5c0b7855ca (patch)
tree4ccc853458a1a0aea4947cf9e49adbb6913b4b42
parent7a85249617d555d5ecd8fc9dcd6566c28f393918 (diff)
fix: stop windows server over named pipes
refs #1113
-rw-r--r--docs/next/CHANGELOG.md1
-rw-r--r--src/session.rs23
2 files changed, 22 insertions, 2 deletions
diff --git a/docs/next/CHANGELOG.md b/docs/next/CHANGELOG.md
index 8701dd24..36eade52 100644
--- a/docs/next/CHANGELOG.md
+++ b/docs/next/CHANGELOG.md
@@ -19,6 +19,7 @@
### Fixed
- `herdr --remote` now prints clean remote attach failures and SSH authentication guidance instead of Rust Debug-formatted I/O errors when SSH authentication is denied. (#1034)
+- `herdr server stop` now stops Windows named-pipe servers instead of failing with `named pipes do not support I/O timeouts`. (#1113)
- `herdr server stop` now waits until both server sockets are unreachable before returning, avoiding an immediate first-start failure when restarting right after replacing the binary.
- macOS `herdr --remote` clients now bridge Finder-dropped image files to the remote pane instead of forwarding the local file path as typed text. (#828)
- Grok Build agent detection now tracks the current Grok Build UI: panes report working while responses, tools, and subagents run, and blocked on permission prompts and question dialogs, instead of falling back to idle mid-turn. (#1017)
diff --git a/src/session.rs b/src/session.rs
index 4dfb2658..8abda3d1 100644
--- a/src/session.rs
+++ b/src/session.rs
@@ -325,7 +325,7 @@ fn send_stop_request(
return Ok(None);
};
if let Err(err) = stream.set_send_timeout(Some(write_timeout)) {
- if err.kind() != std::io::ErrorKind::InvalidInput {
+ if !stop_timeout_error_allows_wait(&err) {
return Err(err.to_string());
}
}
@@ -354,7 +354,7 @@ fn send_stop_request_inner(
return Ok(None);
};
if let Err(err) = stream.set_recv_timeout(Some(read_timeout)) {
- if err.kind() == std::io::ErrorKind::InvalidInput {
+ if stop_timeout_error_allows_wait(&err) {
return Ok(None);
}
return Err(err);
@@ -368,6 +368,11 @@ fn send_stop_request_inner(
Ok(Some(line))
}
+fn stop_timeout_error_allows_wait(err: &std::io::Error) -> bool {
+ err.kind() == std::io::ErrorKind::InvalidInput
+ || (cfg!(windows) && err.kind() == std::io::ErrorKind::Unsupported)
+}
+
fn stop_request_error_allows_wait(err: &std::io::Error) -> bool {
matches!(
err.kind(),
@@ -505,6 +510,20 @@ mod tests {
}
#[test]
+ fn stop_timeout_invalid_input_waits_for_socket_state() {
+ let err = std::io::Error::from(std::io::ErrorKind::InvalidInput);
+
+ assert!(stop_timeout_error_allows_wait(&err));
+ }
+
+ #[test]
+ fn stop_timeout_unsupported_waits_for_socket_state_only_on_windows() {
+ let err = std::io::Error::from(std::io::ErrorKind::Unsupported);
+
+ assert_eq!(stop_timeout_error_allows_wait(&err), cfg!(windows));
+ }
+
+ #[test]
fn socket_timeouts_are_never_zero_duration() {
assert_eq!(socket_timeout_from_remaining(Duration::ZERO), None);
assert_eq!(