Handling reqwest errors

I'm struggling to properly handle errors returned from an API call via reqwest. The code below works fine:

pub fn get_weather(cty: &Vec<String>, st: &Vec<String>) -> Result<WeatherResponse, Box<dyn std::error::Error>> {
    // some stuff here
    let url = reqwest::Url::parse_with_params(url, &params)?;
    let res: WeatherResponse = blocking::get(url)?.json()?;
    Ok(res)
}

match api_call::get_weather(&input.city, &input.state) {
    Ok(res) => // do some stuff,
    Err(err) => println!("Error: {}", err)
}

If the API returns an error, I get something like this:

Error: error decoding response body: invalid type: string "404", expected i32 at line 1 column 12

The API though returns nice error messages like this:

{
    "cod": "404",
    "message": "city not found"
}

I believe what I need to do is something to this effect:

pub fn get_weather(cty: &Vec<String>, st: &Vec<String>) -> Result<WeatherResponse, Box<dyn std::error::Error>> {
    // some stuff here
    let url = reqwest::Url::parse_with_params(url, &params)?;
    let res = match blocking::get(url)? {
        Ok(res) => {
            let res: WeatherResponse = res.json()?;
            Ok(res)
        },
        Err(err) => // parse to a custom error struct
    };
}

But it says:

  |
78 |     let res = match blocking::get(url)? {
   |                     ------------------- this expression has type `reqwest::blocking::Response`
79 |         Ok(res) => {
   |         ^^^^^^^ expected struct `reqwest::blocking::Response`, found enum `std::result::Result`
   |
   = note: expected struct `reqwest::blocking::Response`
                found enum `std::result::Result<_, _>`

What's the proper way to handle this?

EDIT: I think I see what the culprit is here now:

let res = match blocking::get(url)?

It sounds like you have a struct with cod having the type i32, but it is actually a string, not an integer.

1 Like

I tried to create an enum and then use .json to deserialize to the appropriate variant (one representing a successful response and the other representing an error) but it didn't work. Do you know if such a thing is possible?

It would look roughly like this:

#[derive(Deserialize)]
#[serde(untagged)]
enum Response {
    Success(WeatherResponse),
    Error(Error),
}

#[derive(Deserialize)]
struct Error {
    code: String,
    message: String,
}