@smbcloud / cli / commits / 4271700

Improve error handling for login with better message extraction

Seto Elkahfi committed May 14, 2026 at 21:15 UTC 4271700b3278f369404d6634546004a438196206
1 file changed +60 -17
crates/smbcloud-network/src/network.rs
+60 -17
@@ -160,9 +160,8 @@ pub async fn request_login(builder: RequestBuilder) -> Result<AccountStatus, Err
160 Ok(AccountStatus::NotFound)
161 }
162 (StatusCode::UNPROCESSABLE_ENTITY, _) => {
163 - // Account found but email not verified / password not set.
164 - let result: SmbAuthorization = match response.json().await {
165 - Ok(res) => res,
163 + let body_text = match response.text().await {
164 + Ok(text) => text,
165 Err(_) => {
166 return Err(ErrorResponse::Error {
167 error_code: ErrorCode::NetworkError,
@@ -170,21 +169,42 @@ pub async fn request_login(builder: RequestBuilder) -> Result<AccountStatus, Err
169 });
170 }
171 };
173 - // println!("Result: {:#?}", &result);
172 +
173 + let result: SmbAuthorization = match serde_json::from_str(&body_text) {
174 + Ok(res) => res,
175 + Err(_) => {
176 + // Try generic JSON for a useful message.
177 + let message = serde_json::from_str::<serde_json::Value>(&body_text)
178 + .ok()
179 + .and_then(|v| {
180 + v.get("message")
181 + .and_then(serde_json::Value::as_str)
182 + .map(ToOwned::to_owned)
183 + })
184 + .unwrap_or_else(|| ErrorCode::NetworkError.message(None).to_string());
185 + return Err(ErrorResponse::Error {
186 + error_code: ErrorCode::NetworkError,
187 + message,
188 + });
189 + }
190 + };
191 +
192 let error_code = match result.error_code {
193 Some(code) => code,
194 None => {
195 + // No error_code but we have a message — return it as Incomplete with a
196 + // generic unverified status so the client can display the message.
197 return Err(ErrorResponse::Error {
198 error_code: ErrorCode::NetworkError,
179 - message: ErrorCode::NetworkError.message(None).to_string(),
199 + message: result.message,
200 });
201 }
202 };
203 Ok(AccountStatus::Incomplete { status: error_code })
204 }
205 (StatusCode::UNAUTHORIZED, _) => {
186 - let result: SmbAuthorization = match response.json().await {
187 - Ok(res) => res,
206 + let body_text = match response.text().await {
207 + Ok(text) => text,
208 Err(_) => {
209 return Err(ErrorResponse::Error {
210 error_code: ErrorCode::NetworkError,
@@ -192,18 +212,41 @@ pub async fn request_login(builder: RequestBuilder) -> Result<AccountStatus, Err
212 });
213 }
214 };
195 - let error_code = match result.error_code {
196 - Some(code) => code,
197 - None => {
198 - return Err(ErrorResponse::Error {
199 - error_code: ErrorCode::NetworkError,
200 - message: ErrorCode::NetworkError.message(None).to_string(),
201 - });
202 - }
203 - };
215 +
216 + // Try parsing as SmbAuthorization first.
217 + if let Ok(result) = serde_json::from_str::<SmbAuthorization>(&body_text) {
218 + let error_code = match result.error_code {
219 + Some(code) => code,
220 + None => {
221 + // Response parsed but no error_code — surface the server's message.
222 + return Err(ErrorResponse::Error {
223 + error_code: ErrorCode::Unauthorized,
224 + message: result.message,
225 + });
226 + }
227 + };
228 + return Err(ErrorResponse::Error {
229 + error_code: ErrorCode::Unauthorized,
230 + message: error_code.to_string(),
231 + });
232 + }
233 +
234 + // Fallback: try to extract message or error from a generic JSON object.
235 + if let Ok(generic) = serde_json::from_str::<serde_json::Value>(&body_text) {
236 + let extracted = generic
237 + .get("message")
238 + .or_else(|| generic.get("error"))
239 + .and_then(serde_json::Value::as_str)
240 + .unwrap_or("Sign in failed.");
241 + return Err(ErrorResponse::Error {
242 + error_code: ErrorCode::Unauthorized,
243 + message: extracted.to_string(),
244 + });
245 + }
246 +
247 Err(ErrorResponse::Error {
248 error_code: ErrorCode::Unauthorized,
206 - message: error_code.to_string(),
249 + message: "Sign in failed.".to_string(),
250 })
251 }
252 (status, _) => parse_error_response(response)