Repository: mkdocs/mkdocs

Issue:
serve command silently fails with build error(s) and then immediately exits
✅ bug 🐛
⬜ feature

## Description

Calling `mkdocs serve ...` successfully builds the documentation, but never (successfully) starts a server. It just exits.

**Tip:** this could potentially be an issue with the "minidocks/mkdocs:latest" docker image, so I'll follow-up on that.

## Steps to reproduce

1. Start a docker container with MkDocs and your project's files under "/app": `docker run --rm -it -v $(pwd):/app -w /app --entrypoint="/bin/bash" minidocks/mkdocs:latest`
2. Wait for docker to start and provide the bash prompt...
2. Within the docker container, run `mkdocs serve --livereload --watch-theme -a 0.0.0.0:8000 -v`

## Files and logs

**mkdocs.yml**
```yaml
docs_dir: "src/docs"
site_name: "Awesome Enterprise"
site_url: "https://github.com/ourchitecture/awesome-enterprise"
site_author: "Ourchitecture"
copyright: "Creative Commons Zero v1.0 Universal (CC0-1.0)"
theme:
  name: "material"
  features:
    - header.autohide

nav:
  - "Home": "index.md"

extra:
  social:
    - icon: "fontaweseome/brands/github"
      link: "https://github.com/ourchitecture/awesome-enterprise"
      name: "GitHub"
```

**src/docs/index.md**
```
# Hello world
```

**Output log**
```
mkdocs serve --livereload --watch-theme -a 0.0.0.0:8000 -v
INFO     -  Building documentation...
DEBUG    -  Loading configuration file: /app/mkdocs.yml
DEBUG    -  Loaded theme configuration for 'material' from '/usr/lib/python3.9/site-packages/material/mkdocs_theme.yml': {'language': 'en', 'direction': None,
            'features': [], 'palette': {'primary': None, 'accent': None}, 'font': {'text': 'Roboto', 'code': 'Roboto Mono'}, 'icon': None, 'favicon':
            'assets/images/favicon.png', 'include_search_page': False, 'search_index_only': True, 'static_templates': ['404.html']}
WARNING  -  Config value: 'dev_addr'. Warning: The use of the IP address '0.0.0.0' suggests a production environment or the use of a proxy to connect to the MkDocs     
            server. However, the MkDocs' server is intended for local development purposes only. Please use a third party production-ready server instead.
DEBUG    -  Config value: 'config_file_path' = '/app/mkdocs.yml'
DEBUG    -  Config value: 'site_name' = 'Awesome Enterprise'
DEBUG    -  Config value: 'nav' = [{'Home': 'index.md'}]
DEBUG    -  Config value: 'pages' = None
DEBUG    -  Config value: 'site_url' = 'https://github.com/ourchitecture/awesome-enterprise/'
DEBUG    -  Config value: 'site_description' = None
DEBUG    -  Config value: 'site_author' = 'Ourchitecture'
DEBUG    -  Config value: 'theme' = Theme(name='material', dirs=['/usr/lib/python3.9/site-packages/material', '/usr/lib/python3.9/site-packages/mkdocs/templates'],     
            static_templates=['404.html', 'sitemap.xml'], locale=Locale('en'), language='en', direction=None, features=['header.autohide'], palette={'primary': None,   
            'accent': None}, font={'text': 'Roboto', 'code': 'Roboto Mono'}, icon=None, favicon='assets/images/favicon.png', include_search_page=False,
            search_index_only=True)
DEBUG    -  Config value: 'docs_dir' = '/app/src/docs'
DEBUG    -  Config value: 'site_dir' = '/tmp/mkdocs_frudqxir'
DEBUG    -  Config value: 'copyright' = 'Creative Commons Zero v1.0 Universal (CC0-1.0)'
DEBUG    -  Config value: 'google_analytics' = None
DEBUG    -  Config value: 'dev_addr' = Address(host='0.0.0.0', port=8000)
DEBUG    -  Config value: 'use_directory_urls' = True
DEBUG    -  Config value: 'repo_url' = ''
DEBUG    -  Config value: 'repo_name' = ''
DEBUG    -  Config value: 'edit_uri' = ''
DEBUG    -  Config value: 'extra_css' = []
DEBUG    -  Config value: 'extra_javascript' = []
DEBUG    -  Config value: 'extra_templates' = []
DEBUG    -  Config value: 'markdown_extensions' = ['toc', 'tables', 'fenced_code']
DEBUG    -  Config value: 'mdx_configs' = {}
DEBUG    -  Config value: 'strict' = False
DEBUG    -  Config value: 'remote_branch' = 'gh-pages'
DEBUG    -  Config value: 'remote_name' = 'origin'
DEBUG    -  Config value: 'extra' = {'social': [{'icon': 'fontaweseome/brands/github', 'link': 'https://github.com/ourchitecture/awesome-enterprise', 'name':
            'GitHub'}]}
DEBUG    -  Config value: 'plugins' = PluginCollection([('search', <mkdocs.contrib.search.SearchPlugin object at 0x7f0be1f273d0>)])
INFO     -  Cleaning site directory
DEBUG    -  Looking for translations for locale 'en'
DEBUG    -  No translations found here: '/usr/lib/python3.9/site-packages/mkdocs/templates/locales'
DEBUG    -  No translations found here: '/usr/lib/python3.9/site-packages/material/locales'
DEBUG    -  Reading markdown pages.
DEBUG    -  Reading: index.md
DEBUG    -  Copying static assets.
DEBUG    -  Copying media file: 'assets/images/favicon.png'
DEBUG    -  Copying media file: 'assets/javascripts/bundle.82b56eb2.min.js'
DEBUG    -  Copying media file: 'assets/javascripts/bundle.82b56eb2.min.js.map'
DEBUG    -  Copying media file: 'assets/javascripts/lunr/min/lunr.ar.min.js'
DEBUG    -  Copying media file: 'assets/javascripts/lunr/min/lunr.da.min.js'
DEBUG    -  Copying media file: 'assets/javascripts/lunr/min/lunr.de.min.js'
DEBUG    -  Copying media file: 'assets/javascripts/lunr/min/lunr.du.min.js'
DEBUG    -  Copying media file: 'assets/javascripts/lunr/min/lunr.es.min.js'
DEBUG    -  Copying media file: 'assets/javascripts/lunr/min/lunr.fi.min.js'
DEBUG    -  Copying media file: 'assets/javascripts/lunr/min/lunr.fr.min.js'
DEBUG    -  Copying media file: 'assets/javascripts/lunr/min/lunr.hi.min.js'
DEBUG    -  Copying media file: 'assets/javascripts/lunr/min/lunr.hu.min.js'
DEBUG    -  Copying media file: 'assets/javascripts/lunr/min/lunr.it.min.js'
DEBUG    -  Copying media file: 'assets/javascripts/lunr/min/lunr.ja.min.js'
DEBUG    -  Copying media file: 'assets/javascripts/lunr/min/lunr.jp.min.js'
DEBUG    -  Copying media file: 'assets/javascripts/lunr/min/lunr.multi.min.js'
DEBUG    -  Copying media file: 'assets/javascripts/lunr/min/lunr.nl.min.js'
DEBUG    -  Copying media file: 'assets/javascripts/lunr/min/lunr.no.min.js'
DEBUG    -  Copying media file: 'assets/javascripts/lunr/min/lunr.pt.min.js'
DEBUG    -  Copying media file: 'assets/javascripts/lunr/min/lunr.ro.min.js'
DEBUG    -  Copying media file: 'assets/javascripts/lunr/min/lunr.ru.min.js'
DEBUG    -  Copying media file: 'assets/javascripts/lunr/min/lunr.stemmer.support.min.js'
DEBUG    -  Copying media file: 'assets/javascripts/lunr/min/lunr.sv.min.js'
DEBUG    -  Copying media file: 'assets/javascripts/lunr/min/lunr.th.min.js'
DEBUG    -  Copying media file: 'assets/javascripts/lunr/min/lunr.tr.min.js'
DEBUG    -  Copying media file: 'assets/javascripts/lunr/min/lunr.vi.min.js'
DEBUG    -  Copying media file: 'assets/javascripts/lunr/min/lunr.zh.min.js'
DEBUG    -  Copying media file: 'assets/javascripts/lunr/tinyseg.js'
DEBUG    -  Copying media file: 'assets/javascripts/lunr/wordcut.js'
DEBUG    -  Copying media file: 'assets/javascripts/workers/search.477d984a.min.js'
DEBUG    -  Copying media file: 'assets/javascripts/workers/search.477d984a.min.js.map'
DEBUG    -  Copying media file: 'assets/stylesheets/main.ca7ac06f.min.css'
DEBUG    -  Copying media file: 'assets/stylesheets/main.ca7ac06f.min.css.map'
DEBUG    -  Copying media file: 'assets/stylesheets/palette.f1a3b89f.min.css'
DEBUG    -  Copying media file: 'assets/stylesheets/palette.f1a3b89f.min.css.map'
DEBUG    -  Building theme template: 404.html
.icons/fontaweseome/brands/github.svg
```

👆🏼 💥  The above output stops and so does the `mkdocs serve ...` command. No additional info and no running server.
fix: abort with an obvious error message
Generic errors like `TemplateNotFound` (issue #2623) are not clear

It seems there are no tests for "serve.py".

Signed-off-by: Eric Swanson <ericis@users.noreply.github.com>


Discussion:
I have confirmed this without Docker as well:

Running the command `mkdocs serve` results in the output below and immediate exit.

```
INFO     -  Building documentation...
INFO     -  Cleaning site directory
.icons/fontaweseome/brands/github.svg
```

- Windows 10 Professional
- Python 3.9.5
- pip 21.3
- mkdocs and mkdocs-material

    ```
    Name: mkdocs
    Version: 1.2.3
    Summary: Project documentation with Markdown.
    Home-page: https://www.mkdocs.org
    Author: Tom Christie
    Author-email: tom@tomchristie.com
    License: BSD
    Location: c:\users\iseri\appdata\local\programs\python\python39\lib\site-packages
    Requires: click, ghp-import, importlib-metadata, Jinja2, Markdown, mergedeep, packaging, PyYAML, pyyaml-env-tag, watchdog
    Required-by: mkdocs-material
    ---
    Name: mkdocs-material
    Version: 7.3.3
    Summary: A Material Design theme for MkDocs
    Home-page: https://squidfunk.github.io/mkdocs-material/
    Author: Martin Donath
    Author-email: martin.donath@squidfunk.com
    License: MIT
    Location: c:\users\iseri\appdata\local\programs\python\python39\lib\site-packages
    Requires: jinja2, markdown, mkdocs, mkdocs-material-extensions, pygments, pymdown-extensions
    Required-by:
    ```
🏁 **Root cause found!** 🎈

This "extra" configuration for the "social.icon" was miss-spelled as "fontaweseome" instead of "fontawesome".

🐛 The output of the "serve" command actually printed the path to this missing icon as ".icons/fontaweseome/brands/github.svg", but never reported an error. It simply exited silently.

```
extra:
  social:
    - icon: "fontaweseome/brands/github"
      link: "https://github.com/ourchitecture/awesome-enterprise"
      name: "GitHub"
```
Thanks for the report.

So, as you probably see, the initial title of the issue is not right. This *is* actually a build error, just that MkDocs reports it in a super strange way.

This is the resulting traceback:

```
Traceback (most recent call last):
  File ".../mkdocs/commands/build.py", line 306, in build
    _build_theme_template(template, env, files, config, nav)
  File ".../mkdocs/commands/build.py", line 111, in _build_theme_template
    output = _build_template(template_name, template, files, config, nav)
  File ".../mkdocs/commands/build.py", line 90, in _build_template
    output = template.render(context)
  File ".../jinja2/environment.py", line 1090, in render
    self.environment.handle_exception()
  File ".../jinja2/environment.py", line 832, in handle_exception
    reraise(*rewrite_traceback_stack(source=source))
  File ".../jinja2/_compat.py", line 28, in reraise
    raise value.with_traceback(tb)
  File ".../material/404.html", line 4, in top-level template code
    {% extends "main.html" %}
  File ".../material/main.html", line 4, in top-level template code
    {% extends "base.html" %}
  File ".../material/base.html", line 189, in top-level template code
    {% block footer %}
  File ".../material/base.html", line 190, in block "footer"
    {% include "partials/footer.html" %}
  File ".../material/partials/footer.html", line 58, in top-level template code
    {% include "partials/social.html" %}
  File ".../material/partials/social.html", line 13, in top-level template code
    {% include ".icons/" ~ social.icon ~ ".svg" %}
  File ".../jinja2/loaders.py", line 197, in get_source
    raise TemplateNotFound(template)
jinja2.exceptions.TemplateNotFound: .icons/fontaweseome/brands/github.svg
```

And this is why it is being wrongly suppressed:

https://github.com/pallets/jinja/blob/417f822196f66155e8c121e5229cc12a6b02ce14/src/jinja2/exceptions.py#L18
```python
class TemplateNotFound(IOError, LookupError, TemplateError):
```

https://github.com/mkdocs/mkdocs/blob/fd0e9dedd27e4cb628ab98ff2723165110b38ed2/mkdocs/commands/serve.py#L86-L88
@oprypin I updated the title based on your comment.
So, while the "best" solution might be for the [jinja project](https://github.com/pallets/jinja/) to provide a better error message, it would be immediately actionable for this `mkdocs` project to throw a more detailed error to [the code @oprypin mentioned](https://github.com/mkdocs/mkdocs/blob/fd0e9dedd27e4cb628ab98ff2723165110b38ed2/mkdocs/commands/serve.py#L86-L88):

Something like...

```python
# Provide more helpful error messaging
# See: https://github.com/mkdocs/mkdocs/issues/2623
except TemplateNotFound as templateError:  # pragma: no cover
        # Avoid ugly, unhelpful traceback
        raise Abort("Template file not found: " + str(templateError))
except OSError as e:  # pragma: no cover
        # Avoid ugly, unhelpful traceback
        raise Abort(str(e))
```

Or, possibly improve all cases with better generic error messaging...

```python
except OSError as e:  # pragma: no cover
        # Avoid ugly, unhelpful traceback
        raise Abort("Unexpected error: " + str(e))
```


Resolving patch:
diff --git a/mkdocs/commands/serve.py b/mkdocs/commands/serve.py
--- a/mkdocs/commands/serve.py
+++ b/mkdocs/commands/serve.py
@@ -4,6 +4,8 @@
 from urllib.parse import urlsplit
 from os.path import isdir, isfile, join
 
+import jinja2.exceptions
+
 from mkdocs.commands.build import build
 from mkdocs.config import load_config
 from mkdocs.exceptions import Abort
@@ -93,9 +95,12 @@ def error_handler(code):
             log.info("Shutting down...")
         finally:
             server.shutdown()
+    except jinja2.exceptions.TemplateError:
+        # This is a subclass of OSError, but shouldn't be suppressed.
+        raise
     except OSError as e:  # pragma: no cover
         # Avoid ugly, unhelpful traceback
-        raise Abort(str(e))
+        raise Abort(f'{type(e).__name__}: {e}')
     finally:
         if isdir(site_dir):
             shutil.rmtree(site_dir)


Test patch: