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