Skip to main content

irontide_session/
error.rs

1/// Convenience alias for `Result<T, Error>`.
2pub type Result<T> = std::result::Result<T, Error>;
3
4/// Errors that can occur during session operations.
5#[derive(Debug, thiserror::Error)]
6pub enum Error {
7    /// Wire protocol error from the peer connection layer.
8    #[error("wire: {0}")]
9    Wire(#[from] irontide_wire::Error),
10
11    /// Storage/disk I/O error.
12    #[error("storage: {0}")]
13    Storage(#[from] irontide_storage::Error),
14
15    /// Tracker communication error.
16    #[error("tracker: {0}")]
17    Tracker(#[from] irontide_tracker::Error),
18
19    /// DHT operation error.
20    #[error("DHT: {0}")]
21    Dht(#[from] irontide_dht::Error),
22
23    /// Core library error (metainfo parsing, hashing, etc.).
24    #[error("core: {0}")]
25    Core(#[from] irontide_core::Error),
26
27    /// Peer connection failed.
28    #[error("connection: {0}")]
29    Connection(
30        /// Error description.
31        String,
32    ),
33
34    /// Received metadata whose info hash does not match the expected value.
35    #[error("metadata info_hash mismatch")]
36    MetadataHashMismatch,
37
38    /// A piece failed hash verification.
39    #[error("piece {index} hash verification failed")]
40    PieceHashFailed {
41        /// Zero-based piece index that failed verification.
42        index: u32,
43    },
44
45    /// Received invalid peer data from a tracker or PEX message.
46    #[error("invalid peer data: {0}")]
47    InvalidPeerData(
48        /// Description of the invalid data.
49        String,
50    ),
51
52    /// The requested torrent is not in the session.
53    #[error("torrent not found: {0}")]
54    TorrentNotFound(
55        /// Info hash of the missing torrent.
56        irontide_core::Id20,
57    ),
58
59    /// Attempted to add a torrent that already exists in the session.
60    #[error("duplicate torrent: {0}")]
61    DuplicateTorrent(
62        /// Info hash of the duplicate torrent.
63        irontide_core::Id20,
64    ),
65
66    /// The session has reached its maximum torrent capacity.
67    #[error("session at capacity ({0} torrents)")]
68    SessionAtCapacity(
69        /// Current number of torrents in the session.
70        usize,
71    ),
72
73    /// Torrent metadata has not been received yet (magnet link still resolving).
74    #[error("metadata not yet available for {0}")]
75    MetadataNotReady(
76        /// Info hash of the torrent awaiting metadata.
77        irontide_core::Id20,
78    ),
79
80    /// File index is out of range for the torrent.
81    #[error("file index {index} out of range (torrent has {count} files)")]
82    InvalidFileIndex {
83        /// The requested file index.
84        index: usize,
85        /// Total number of files in the torrent.
86        count: usize,
87    },
88
89    /// Attempted to stream a file that has `Skip` priority.
90    #[error("file has Skip priority (index {index})")]
91    FileSkipped {
92        /// The file index with `Skip` priority.
93        index: usize,
94    },
95
96    /// Piece index is out of range for the torrent.
97    #[error("piece index {index} out of range (torrent has {num_pieces} pieces)")]
98    InvalidPieceIndex {
99        /// The requested piece index.
100        index: u32,
101        /// Total number of pieces in the torrent.
102        num_pieces: u32,
103    },
104
105    /// Configuration error (e.g. invalid proxy settings).
106    #[error("configuration error: {0}")]
107    Config(
108        /// Error description.
109        String,
110    ),
111
112    /// Settings validation failed.
113    #[error("invalid settings: {0}")]
114    InvalidSettings(
115        /// Description of the invalid setting.
116        String,
117    ),
118
119    /// The session is shutting down and cannot accept new commands.
120    #[error("session shutting down")]
121    Shutdown,
122
123    /// DHT is disabled in session settings.
124    #[error("DHT is disabled")]
125    DhtDisabled,
126
127    /// Peer read timeout — no wire message received within the configured duration.
128    #[error("peer read timeout ({0}s)")]
129    PeerReadTimeout(
130        /// Configured timeout in seconds.
131        u64,
132    ),
133
134    /// Peer data contribution timeout — no Piece message received within the configured duration.
135    #[error("peer data timeout ({0}s)")]
136    PeerDataTimeout(
137        /// Configured timeout in seconds.
138        u64,
139    ),
140
141    /// Peer write timeout — outgoing message could not be sent within the configured duration.
142    #[error("peer write timeout ({0}s)")]
143    PeerWriteTimeout(
144        /// Configured timeout in seconds.
145        u64,
146    ),
147
148    /// M182: per-channel backpressure queue exceeded its cap. Treated
149    /// as a soft stall — the downstream consumer (requester or
150    /// `TorrentActor`) is too slow and the wire reader would otherwise
151    /// stall TCP for all peers on this connection. See M182 audit
152    /// `docs/investigations/2026-05-02-m182-stall-audit/INVESTIGATION.md`.
153    #[error("peer backpressure overflow ({0})")]
154    PeerBackpressureOverflow(
155        /// Channel name (`dispatch_tx` or `event_tx`).
156        &'static str,
157    ),
158
159    /// I2P SAM protocol error.
160    #[error("I2P: {0}")]
161    I2p(#[from] crate::i2p::SamError),
162
163    /// Operating system I/O error.
164    #[error("I/O: {0}")]
165    Io(#[from] std::io::Error),
166
167    /// M170: caller specified a category that is not in the registry.
168    /// The qBt API maps this to 409 Conflict.
169    #[error("category not found: {0}")]
170    CategoryNotFound(
171        /// The unknown category name.
172        String,
173    ),
174
175    /// M170: an add arrived while the same info hash is still being
176    /// removed by a `deleteFiles=true` delete. Caller should retry
177    /// shortly. Mapped to 409 Conflict by the qBt API.
178    #[error("torrent is being removed, retry later")]
179    TorrentBeingRemoved(
180        /// Info hash currently in the delete grace period.
181        irontide_core::Id20,
182    ),
183
184    /// M170: category registry persistence error.
185    #[error("category registry: {0}")]
186    CategoryRegistry(
187        /// Error description from the category manager.
188        String,
189    ),
190
191    /// M173 Lane B (B11): a concurrent `apply_settings` call won the
192    /// in-flight guard; this caller must retry shortly. The qBt v2
193    /// `setPreferences` handler maps this to HTTP 409 Conflict.
194    #[error("apply_settings: concurrent reconfig in flight, retry shortly")]
195    ConcurrentReconfig,
196}