Thread 'actix-rt|system:0|arbiter:2' panicked at 'called `Option::unwrap()` on a `None` value'

Hello, I am making a simple twitter-like app but anonymous with Actix and rusqlite, I have a page /id/, where you give the post id, and it returns the post, if you use an id that doesn't exist, it gives this error in the terminal while running:
thread 'actix-rt|system:0|arbiter:2' panicked at 'called `Option::unwrap()` on a `None` value', src/main.rs:59:82

then on even a valid request it gives this error

thread 'actix-rt|system:0|arbiter:3' panicked at 'called `Result::unwrap()` on an `Err` value: PoisonError { .. }', src/main.rs:43:33
thread 'actix-rt|system:0|arbiter:4' panicked at 'called `Result::unwrap()` on an `Err` value: PoisonError { .. }', src/main.rs:43:33

It creates an error on the line:

let conn_lock = conn.lock().unwrap();

Which shouldn't be the culprit, as it's just a connection to the db,
Here's the whole code for context:

mod sql;

use std::sync::{Mutex, Arc};

use actix_web::{get, post, web, App, HttpResponse, HttpServer, Responder, http::header::q};
use rand::Rng;
use rusqlite::{params, Connection, Result};

#[derive(Debug)]
struct Text {
    id: i32,
    text: String,
}

impl ToString for Text {
    fn to_string(&self) -> String {
        "text".to_string()
    }
}

#[get("/")]
async fn hello(conn: web::Data<Arc<Mutex<Connection>>>) -> impl Responder {
    let conn_lock = conn.lock().unwrap();
    let mut query = conn_lock.prepare("SELECT id, text FROM texts").expect("REASON");
    let texts_iter = query.query_map([], |row| {
        Ok(Text {   
            id: row.get(0)?,
            text: row.get(1)?,
        })
        }).unwrap();
    
    let mut texts: Vec<Text> = Vec::new();
    for text in texts_iter {
        // println!("Found person {:?}", person.unwrap());
        texts.push(text.unwrap());
    }

    HttpResponse::Ok().body(" ")
}

#[get("/id/{id}")]
async fn get_by_id(id: web::Path<String>, conn: web::Data<Arc<Mutex<Connection>>>) -> impl Responder {
    let conn_lock = conn.lock().unwrap();
    let mut query = conn_lock.prepare("SELECT id, text FROM texts").expect("REASON");
    let texts_iter = query.query_map([], |row| {
        Ok(Text {   
            id: row.get(0)?,
            text: row.get(1)?,
        })
        }).unwrap();
    
    let mut texts: Vec<Text> = Vec::new();
    for text in texts_iter {
        // println!("Found person {:?}", person.unwrap());
        texts.push(text.unwrap());
    }
    


    HttpResponse::Ok().body(texts.get(id.into_inner().parse::<usize>().unwrap()).unwrap().text.clone())
}
#[post("/post_text/")]
async fn post_text(req_body: String, conn: web::Data<Arc<Mutex<Connection>>>) -> impl Responder {
    let conn_lock = conn.lock().unwrap();

    conn_lock.execute(
        "
        INSERT INTO texts (text) VALUES (?)",
        [&req_body],
    ).expect("Couldn't Post");

    ""
}

#[post("/id_from_text")]
async fn echo(req_body: String, conn: web::Data<Arc<Mutex<Connection>>>) -> impl Responder {
    // let conn_lock = conn.lock().unwrap();

    // let mut query = conn_lock.prepare(&("SELECT * FROM texts WHERE text LIKE".to_owned() + &req_body)).expect("REASON");
    // let texts_iter = query.query_map([], |row| {
    //     Ok(Text {   
    //         id: row.get(0)?,
    //         text: row.get(1)?,
    //     })
    //     }).unwrap();
    
    // let mut texts: Vec<Text> = Vec::new();
    // for text in texts_iter {
    //     // println!("Found person {:?}", person.unwrap());
    //     texts.push(text.unwrap());
    // }
    

    ""
}

// fn init_db() -> Result<(), rusqlite::Error> {

//     Ok(())
// }

// //http://127.0.0.1:8080/
#[actix_web::main]
async fn main() -> Result<(), Box<dyn std::error::Error>>{
    let conn = Connection::open("./db/main.db")?;


    // conn.execute(
    //     "
    //     CREATE TABLE texts (
    //         id   INTEGER PRIMARY KEY,
    //         text TEXT NOT NULL
    //     )",
    //     (),
    // )?;
    let text = Text {
        id: 0,
        text: "Hello World".to_string(),
    };
    conn.execute(
        "
        INSERT INTO texts (text) VALUES (?)",
        [&text.text],
    )?;

    conn.execute(
        "
        INSERT INTO texts (text) VALUES (?)",
        [&text.text],
    )?;

    // conn.execute(
    //     "INSERT INTO person (name, data) VALUES (?1, ?2)",
    //     (&me.name, &me.data),
    // )?;

    {
        let mut query = conn.prepare("SELECT id, text FROM texts")?;
        let text_iter = query.query_map([], |row| {
            Ok(Text {
                id: row.get(0)?,
                text: row.get(1)?,
            })
        })?;
        

        for text in text_iter {
            println!("Found text {:?}", text.unwrap());
        };
    }

    let conn_data: web::Data<Arc<Mutex<Connection>>> = web::Data::new(Arc::new(Mutex::new(conn)));
    
    HttpServer::new(move || {
        App::new()
            .service(hello)
            .app_data(conn_data.clone())
            .service(post_text)
            .app_data(conn_data.clone())
            .service(get_by_id)
    })
    .bind(("127.0.0.1", 8080))?
    .run()
    .await;
    Ok(())
}

There are so many calls to unwrap in your code, I don't know which one is causing the panic. The panic message contains the line where it occurred (which doesn't match a possible line in your example). If you don't want to panic, don't call unwrap. Handle None values differently, without panicking. Instead, return a http response with a 404 status code, for example.

That's because the thread holding the lock panicked (because you called unwrap() on a Option::None value), causing the Mutex to become poisoned.

1 Like

How would I handle None differently?

There are a lot of ways you can check if something is None or Some(...). I'd probably do it with let else if it is in the middle of the control flow, like:

let Some(x) = your_option_value else {
    println!("your_option_value was None");
    return HttResponse::NotFound().finish();
}

// you can use `x` here like any other let-binding

You could also use match:

match your_option_value {
    Some(x) => { ... }
    None => HttpResponse::NotFound().finish()
}

Or Option::is_none:

if your_option_value.is_none() {
    return HttpResponse::NotFound().finish();
}

// calling your_option_value.unwrap() is safe here because we 
// return early it is `None`

Just to name a few ways not involving unwrap.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.