I have multiple error logs that looks like this message Failed to fetch dataset for data collection game: DbErr(RecordNotFound("Cannot find collected additional data")), continue...
This happens because I do multiple error handlings in different nested functions. How can I make these error messages more cleaner?
That error message looks pretty good to me. It tells you the high level operation that failed as well as the reason for the failure. Why do you think it is not clean?
It is expected that the fmt::Debug formatting of nested errors looks like this. If you don't want that, you should instead use fmt::Display to get a human-friendly message, and follow the Error::source() chain to print those too — this allows you to print a backtrace-like list of error causes instead of parenthetical nesting. (Error-handling libraries like anyhow and eyre often have tools to do this printing for you.)
That said, something strange is going on here, because your line
warn!("Failed to fetch dataset for data collection game: {}, continue...", err);
is asking for Display formatting. It seems like whatever the type of err is (I don't know because you haven't shown us fn create_dataset), it is mixing up Debug and Display in its own Display implementation.
If you don't want the Debug output, then you must specify a different Display format somehow. To make this as easy as possible, use anyhow or one of the other error crates.
The general list is here: std::fmt / Formatting Traits. But for errors, you have only two choices, Debug and Display, and you're already trying to implementDisplay so calling it on self would just be infinite recursion.
In the end, somewhere, you're going to need a match on the contents of the enum. But libraries such as thiserror can create one for you and properly format the error.
Note that in general, when error types wrap other error types, they should make a choice between being transparent or not. Transparent errors are just wrapping other error types and convey no information. Non-transparent errors convey their own information about what error happened, in addition to the wrapped error. In your case you should probably create a non-transparent error so that the user can receive more informative error messages — a std::io::Error by itself is very unhelpful without any information about what operation was attempted.
Transparent errors should:
Implement Display to format exactly the same as the wrapped error.
Implement Error::source() by calling Error::source() on the wrapped error.
Non-transparent errors should:
Implement Display to format only their additional information — for example, in the case of a wrapped IO error, the wrapper could print "failed to open file /some/path".
In either case, when logging or showing a user an error, Error::source() should be used to obtain and print the full chain of errors. This is not the responsibility of the individual error type.