Hi, I have this weird problem where Tokio will crash with the error thread 'tokio-runtime-worker' has overflowed its stack
when an error happens, and you use ?
to propagate it up, AND the Error implement std::fmt::Display
First, the setup. Axum as the framework(hence the tokio usage), using GraphQL(async-graphql) and MongoDB. Async-graphql requires that an Error must implement std::fmt::Display
, so I added this to my error definition:
pub enum ZataError {
DBError(String),
AxumError(String),
NotFound(String),
ValidationError(String),
WrongCredentials,
MissingCredentials,
TokenCreation,
InvalidToken,
}
impl fmt::Display for ZataError {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
write!(f, "{}", self)
}
}
The query that failed looks like this(but it's not really relevant, I put it here just for information):
#[async_graphql::Object]
impl QueryRoot {
/// Returns all available courses starting today and forward. Does not return old courses
async fn course(&self, ctx: &Context<'_>) -> async_graphql::Result<Vec<Course>> {
let db = &ctx.data::<DataSource>()?.db;
let tutors = graphql_get_all_current_course_db(&db).await?;
Ok(tutors)
}
}
The root of my problem was in my Course
struct definition, there was a disparity between the field definition and what the database returned, so when I did the query, it returned an error but I never saw that error since tokio itself crashed with a stack overflow error. Upon searching a bit, I stumbled on someone who had a similar problem and it was because of a reference to "self" in a Display implementation, and it caused some kind of infinite Borg-space-time-distortion loop in tokio while trying to propagate the error using '?'. This strangely looked like the change I had just made to comply with async-graphql requirement. And indeed, if I use 'unwrap()' to make it panic right away, instead of '?' I could see the real error that was field related.
But my question is this, how to prevent tokio crashing when trying to propagate the error up? What is wrong with the Display implementation? I searched online, but all std::fmt::Display
implementation I saw used self
in it. Am I stuck using unwrap here?