hyperium/hyper · error · std::io::Error
io error
Error message
io error
What it means
Produced inside the C FFI IO adapter (src/ffi/io.rs:155) in hyper_io::poll_read. When the user-supplied C read callback returns HYPER_IO_ERROR, hyper reports a std::io::Error of kind Other with the message 'io error' (ffi/io.rs:155-158). This is the FFI boundary: the error originates entirely in the C-side callback, not in hyper. Only reachable via the hyper C API (feature="ffi").
Source
Thrown at src/ffi/io.rs:155
_: *mut hyper_context<'_>,
_buf: *const u8,
_buf_len: size_t,
) -> size_t {
0
}
impl Read for hyper_io {
fn poll_read(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
mut buf: crate::rt::ReadBufCursor<'_>,
) -> Poll<std::io::Result<()>> {
let buf_ptr = unsafe { buf.as_mut() }.as_mut_ptr().cast::<u8>();
let buf_len = buf.remaining();
match (self.read)(self.userdata, hyper_context::wrap(cx), buf_ptr, buf_len) {
HYPER_IO_PENDING => Poll::Pending,
HYPER_IO_ERROR => Poll::Ready(Err(std::io::Error::new(
std::io::ErrorKind::Other,
"io error",
))),
ok => {
// We have to trust that the user's read callback actually
// filled in that many bytes... :(
unsafe { buf.advance(ok) };
Poll::Ready(Ok(()))
}
}
}
}
impl Write for hyper_io {
fn poll_write(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &[u8],View on GitHub (pinned to 084473f728)
Solutions
- Debug the C read callback: log the underlying transport error before returning HYPER_IO_ERROR, and return HYPER_IO_PENDING for non-blocking 'would block' (EAGAIN/EWOULDBLOCK) instead of an error.
- Ensure the callback fills the buffer and returns the byte count on success, 0 only for clean EOF, and HYPER_IO_ERROR solely for genuine failures.
- Reproduce with the smallest C harness around your transport to confirm which condition maps to the error.
Example fix
// before (C callback): returns error on EAGAIN, surfacing as 'io error'
size_t my_read(void* ud, hyper_context* ctx, uint8_t* buf, size_t len) {
ssize_t n = recv(fd, buf, len, 0);
if (n < 0) return HYPER_IO_ERROR; // wrongly reports EAGAIN as fatal
return (size_t)n;
}
// after: distinguish would-block from real failure
size_t my_read(void* ud, hyper_context* ctx, uint8_t* buf, size_t len) {
ssize_t n = recv(fd, buf, len, 0);
if (n < 0 && (errno == EAGAIN || errno == EWOULDBLOCK)) return HYPER_IO_PENDING;
if (n < 0) return HYPER_IO_ERROR; /* genuine failure only */
return (size_t)n;
} Defensive patterns
Strategy: validation
Validate before calling
// In the C read callback, only return HYPER_IO_ERROR on genuine failures.
// Map EAGAIN/EWOULDBLOCK to HYPER_IO_PENDING so hyper awaits readiness.
size_t cb_read(void* ud, hyper_context* ctx, uint8_t* buf, size_t len) {
ssize_t n = recv(((Fd*)ud)->fd, buf, len, 0);
if (n < 0 && (errno == EAGAIN || errno == EWOULDBLOCK)) return HYPER_IO_PENDING;
if (n < 0) { log_errno(); return HYPER_IO_ERROR; }
return (size_t)n;
} Prevention
- Distinguish 'would block' (return HYPER_IO_PENDING) from real errors (HYPER_IO_ERROR) in read callbacks.
- Log errno/TLS errors in the callback before returning HYPER_IO_ERROR so the cause is recoverable.
- Return 0 only for clean EOF; return the byte count for partial reads.
When it happens
Trigger: An application embedding hyper via the C FFI registers a read callback (hyper_io_set_read) that returns HYPER_IO_ERROR on a transport failure; poll_read (ffi/io.rs:153-158) maps that to io::Error. Common with custom transports (curl-style, another HTTP stack bridged in).
Common situations: The C-side transport (e.g. a socket, a TLS impl, or a bridged library) hit an error and the callback signals HYPER_IO_ERROR; mis-handling read returns (returning error for EAGAIN instead of HYPER_IO_PENDING); a transport that does not distinguish 'would block' from real failure.
Related errors
AI-assisted analysis of hyperium/hyper@084473f728 (2026-08-06).
Data as JSON: /data/errors/278fa18409755ad8.json.
Report an issue: GitHub.