#[non_exhaustive]pub enum Error {
Show 15 variants
InvalidData {
message: String,
},
Protocol {
status: NtStatus,
command: Command,
},
Auth {
message: String,
},
Io(Error),
Timeout,
Disconnected,
ReconnectFailed {
attempts: u32,
waited: Duration,
cause: ErrorKind,
reason: String,
},
DurableHandleLost {
path: String,
reason: DurableLoss,
},
DfsReferralRequired {
path: String,
},
Cancelled,
SessionExpired,
FileTooLargeForSingleRead {
size: u64,
requested: u32,
},
CreditStarvation {
needed: u16,
available: u16,
waited: Duration,
},
SendTimeout {
command: Command,
bytes: usize,
waited: Duration,
},
ServerUnresponsive {
silent_for: Duration,
},
}Expand description
Top-level error type for SMB2 operations.
#[non_exhaustive]: new variants appear as the crate learns to tell more
failures apart, so match on it with a _ arm, or branch on
Error::kind instead. Adding a variant is not treated as a breaking
change.
Variants (Non-exhaustive)§
This enum is marked as non-exhaustive
InvalidData
The data is malformed or does not match the expected format.
Protocol
The server returned a non-success NTSTATUS.
Fields
Auth
Authentication failed.
Io(Error)
An I/O or transport error occurred.
Timeout
The operation timed out.
Disconnected
The connection was lost.
ReconnectFailed
Bringing a dead connection back on a fresh socket did not work.
Every attempt failed, or the whole revival ran past
ReconnectPolicy::total_budget,
or a revival failed recently enough that its verdict still stands (see
ReconnectPolicy::failure_cooldown).
The connection is dead and stays dead. Branch on cause to tell “the
credentials are wrong now, ask the user” from “the network is down, try
again later”; reason is display text for a human and ❌ must never be
matched on. Classifies as ErrorKind::ConnectionLost and reports as
retryable — the connection is finished, but the work usually is not,
and re-running the file on a fresh client is the intended response.
Fields
DurableHandleLost
A durable handle could not be claimed back after a reconnect, so the interrupted transfer has to restart rather than resume.
Never a data-safety failure: it is what the client returns instead of
guessing. Branch on reason to tell an expired open (routine) from a
server that handed back the wrong file (alarming, and logged at
error!). Whatever the reason, the response is the same: reopen the
file and write it from the start. Classifies as
ErrorKind::ConnectionLost and reports as retryable.
Fields
reason: DurableLossWhich guarantee did not hold.
DfsReferralRequired
The path requires DFS referral resolution.
The server returned STATUS_PATH_NOT_COVERED, meaning this path
lives on a different server via DFS. The caller can query for a
referral or display a helpful message.
Cancelled
The operation was cancelled by the caller (via progress callback).
SessionExpired
The session expired and reauthentication failed.
The pipeline normally handles STATUS_NETWORK_SESSION_EXPIRED
transparently by reauthenticating. This error surfaces only
when reauthentication itself fails.
FileTooLargeForSingleRead
The file is larger than the single READ that was issued for it, so returning what came back would truncate it.
Returned by Tree::read_file,
Tree::read_file_compound, and
Tree::read_file_compound_sized.
Those paths issue one READ, so a file bigger than that READ asked for
can’t come back whole; rather than silently dropping the tail, they fail
with this. The two ways to hit it:
- The file exceeds the server’s negotiated per-READ maximum
(
MaxReadSize), which is as small as 64 KiB on some servers. - The file outgrew the
expected_sizehanded toread_file_compound_sizedbetween the caller’s scan and the read.
size is the server’s authoritative size from the same round-trip.
Retrying read_file_compound_sized with it fixes the second case only,
where size still fits one READ. When requested is already the
server’s MaxReadSize the retry asks for the same bytes and fails the
same way; Tree::read_file_pipelined
reads any size in a sliding window of chunked READs and always works.
Classifies as ErrorKind::TooLarge.
Fields
CreditStarvation
The server stopped granting credits, so the request could not be sent.
Every SMB2 request spends credits from a budget the server grants and replenishes on each response. This crate never sends beyond that budget (doing so is a protocol violation the server may answer by dropping the connection, or — on some NAS firmware — by going silent). When the budget runs dry the send waits for a grant; this error says the wait ran out.
In practice it means the server has stopped answering while the TCP
connection is still up, so treat it as a dead connection: reconnect.
Classifies as ErrorKind::TimedOut and reports as retryable.
Tune the wait with
Connection::set_credit_wait_timeout.
Fields
SendTimeout
A request could not be handed to the network in time.
This is the send side, not the response side: the bytes never
reached the socket. A socket that stops accepting writes while TCP
stays ESTABLISHED produces this, and so does a queue behind one
such write.
The distinction from Error::Timeout matters when reading logs. A
Timeout means the server was asked and said nothing; a SendTimeout
means the server was never asked, so nothing about the server can be
inferred from it. A 2026-08-01 wedge was misread as server silence for
exactly this reason: ~700 requests sat registered as in-flight with
zero bytes on the wire.
The connection is torn down when this fires: a write abandoned partway
leaves half a frame on the wire, so the stream can’t be trusted again.
Classifies as ErrorKind::TimedOut and reports as retryable.
Tune with
Connection::set_send_timeout.
Fields
ServerUnresponsive
A request ran out of deadline on a connection the server had gone completely silent on, so the whole session was declared dead.
This is Error::Timeout with a second fact attached. Both mean a
request went unanswered for its full budget; this one adds that the
server put nothing on the wire in the meantime, not even an answer to
the SMB2 ECHO probes the keepalive sends (MS-SMB2 § 2.2.28, a request
that touches no disk and no share). One stuck operation cannot look
like that, so the connection is torn down and every other waiter is
told at once rather than sitting out its own deadline one by one.
The distinction from the other three is what it lets you conclude:
Error::Timeout– this request went unanswered. The connection may be perfectly healthy and the operation merely stuck; retrying it on the same connection is reasonable.Error::SendTimeout– the request never reached the network, so nothing at all follows about the server.Error::Disconnected– the socket itself went away (EOF, reset).ServerUnresponsive– the socket is up and the server is answering nothing at all. Reconnect; retrying on this connection can only fail.
Classifies as ErrorKind::ConnectionLost (the same as
Disconnected, so existing reconnect paths pick it up unchanged) and
reports as retryable. It cannot occur with the keepalive off
(Connection::set_keepalive),
since nothing would then be asking: expect Error::Timeout instead.
Implementations§
Source§impl Error
impl Error
Sourcepub fn invalid_data(msg: impl Into<String>) -> Self
pub fn invalid_data(msg: impl Into<String>) -> Self
Create an InvalidData error with the given message.
Sourcepub fn is_retryable(&self) -> bool
pub fn is_retryable(&self) -> bool
Returns true if this error is potentially transient and
the operation could succeed on retry.
Trait Implementations§
Source§impl Error for Error
impl Error for Error
Source§fn source(&self) -> Option<&(dyn Error + 'static)>
fn source(&self) -> Option<&(dyn Error + 'static)>
1.0.0 · Source§fn description(&self) -> &str
fn description(&self) -> &str
use the Display impl or to_string()