Here’s one way to check if there was an DB error within get_posts():
global $EZSQL_ERROR;
$before = isset( $EZSQL_ERROR ) ? count( $EZSQL_ERROR ) : 0;
$posts = get_posts( $args );
$after = isset( $EZSQL_ERROR ) ? count( $EZSQL_ERROR ) : 0;
if ( empty( $posts ) && $before < $after ) {
// ... DB error(s) within get_posts() when it returns an empty array.
}
Here we check the number of wpdb errors before and after the get_posts() call from the global $EZSQL_ERROR errors collector (src).
But I can imagine that this might give a false positive in some cases though, e.g. if we hook a bad db calls within get_posts() that might not be the reason for empty posts array.
Update. I tested this and noticed that $wpdb->last_error is restored after each $wpdb call. I noticed the global $EZSQL_ERROR array within wpdb::print_error() that is not restored but collects the errors. I therefore updated the answer and replaced $wpdb->last_error with $EZSQL_ERROR.