diff --git a/src/renderer/html_handlebars/hbs_renderer.rs b/src/renderer/html_handlebars/hbs_renderer.rs
index 3d2d1afe94..f477c43ef8 100644
--- a/src/renderer/html_handlebars/hbs_renderer.rs
+++ b/src/renderer/html_handlebars/hbs_renderer.rs
@@ -481,7 +481,13 @@ impl Renderer for HtmlHandlebars {
let mut handlebars = Handlebars::new();
let theme_dir = match html_config.theme {
- Some(ref theme) => ctx.root.join(theme),
+ Some(ref theme) => {
+ let dir = ctx.root.join(theme);
+ if !dir.is_dir() {
+ bail!("theme dir {} does not exist", dir.display());
+ }
+ dir
+ }
None => ctx.root.join("theme"),
};
diff --git a/tests/rendered_output.rs b/tests/rendered_output.rs
index 79c120bbe4..873a622df5 100644
--- a/tests/rendered_output.rs
+++ b/tests/rendered_output.rs
@@ -657,6 +657,57 @@ fn summary_with_markdown_formatting() {
);
}
+/// Ensure building fails if `[output.html].theme` points to a non-existent directory
+#[test]
+fn failure_on_missing_theme_directory() {
+ // 1. Using default theme should work
+ let temp = DummyBook::new().build().unwrap();
+ let book_toml = r#"
+ [book]
+ title = "implicit"
+ src = "src"
+ "#;
+
+ write_file(temp.path(), "book.toml", book_toml.as_bytes()).unwrap();
+ let md = MDBook::load(temp.path()).unwrap();
+ let got = md.build();
+ assert!(got.is_ok());
+
+ // 2. Pointing to a normal directory should work
+ let temp = DummyBook::new().build().unwrap();
+ let created = fs::create_dir(temp.path().join("theme-directory"));
+ assert!(created.is_ok());
+ let book_toml = r#"
+ [book]
+ title = "implicit"
+ src = "src"
+
+ [output.html]
+ theme = "./theme-directory"
+ "#;
+
+ write_file(temp.path(), "book.toml", book_toml.as_bytes()).unwrap();
+ let md = MDBook::load(temp.path()).unwrap();
+ let got = md.build();
+ assert!(got.is_ok());
+
+ // 3. Pointing to a non-existent directory should fail
+ let temp = DummyBook::new().build().unwrap();
+ let book_toml = r#"
+ [book]
+ title = "implicit"
+ src = "src"
+
+ [output.html]
+ theme = "./non-existent-directory"
+ "#;
+
+ write_file(temp.path(), "book.toml", book_toml.as_bytes()).unwrap();
+ let md = MDBook::load(temp.path()).unwrap();
+ let got = md.build();
+ assert!(got.is_err());
+}
+
#[cfg(feature = "search")]
mod search {
use crate::dummy_book::DummyBook;