Repository: mkdocs/mkdocs

Issue:
Unclear issue when trying to build the site
Hi, on two different machines (one macOS, the other Ubuntu with a fresh install of mkdocs), when I try to build the site, I get these very unclear error messages. Would anyone have an idea if the issue is with the mkdocs files or python itself?

Running python 3.10 on the Mac, 3.8 on Ubuntu. Latest version of mkdocs and materials installed. The YAML file was validated, but it is encoded in ASCII and multiple attempts to convert it to UTF8 have mysteriously failed.

It also fails when compiling with GitHub Pages, which is why I tried to debug this on my laptop. There were a few changes made to the code by removing a plug-in I believe, but even if I download old commits that used to build and publish just fine do not work on my computer or on Ubuntu.

I'm really at a loss here.

```
mkdocs build
INFO     -  DeprecationWarning: the imp module is deprecated in favour of importlib; see the module's documentation for alternative uses
              File "/usr/lib/python3/dist-packages/apport/report.py", line 13, in <module>
                import fnmatch, glob, traceback, errno, sys, atexit, locale, imp, stat
              File "/usr/lib/python3.8/imp.py", line 31, in <module>
                warnings.warn("the imp module is deprecated in favour of importlib; "
Traceback (most recent call last):
  File "/usr/local/bin/mkdocs", line 8, in <module>
    sys.exit(cli())
  File "/usr/lib/python3/dist-packages/click/core.py", line 764, in __call__
    return self.main(*args, **kwargs)
  File "/usr/lib/python3/dist-packages/click/core.py", line 717, in main
    rv = self.invoke(ctx)
  File "/usr/lib/python3/dist-packages/click/core.py", line 1137, in invoke
    return _process_result(sub_ctx.command.invoke(sub_ctx))
  File "/usr/lib/python3/dist-packages/click/core.py", line 956, in invoke
    return ctx.invoke(self.callback, **ctx.params)
  File "/usr/lib/python3/dist-packages/click/core.py", line 555, in invoke
    return callback(*args, **kwargs)
  File "/usr/local/lib/python3.8/dist-packages/mkdocs/__main__.py", line 247, in build_command
    cfg = config.load_config(**kwargs)
  File "/usr/local/lib/python3.8/dist-packages/mkdocs/config/base.py", line 363, in load_config
    errors, warnings = cfg.validate()
  File "/usr/local/lib/python3.8/dist-packages/mkdocs/config/base.py", line 228, in validate
    run_failed, run_warnings = self._validate()
  File "/usr/local/lib/python3.8/dist-packages/mkdocs/config/base.py", line 186, in _validate
    self[key] = config_option.validate(value)
  File "/usr/local/lib/python3.8/dist-packages/mkdocs/config/config_options.py", line 146, in validate
    return self.run_validation(value)
  File "/usr/local/lib/python3.8/dist-packages/mkdocs/config/config_options.py", line 937, in run_validation
    name, plugin = self.load_plugin_with_namespace(name, cfg)
  File "/usr/local/lib/python3.8/dist-packages/mkdocs/config/config_options.py", line 974, in load_plugin_with_namespace
    return (name, self.load_plugin(name, config))
  File "/usr/local/lib/python3.8/dist-packages/mkdocs/config/config_options.py", line 1002, in load_plugin
    self.warnings.extend(f"Plugin '{name}' value: '{x}'. Warning: {y}" for x, y in warnings)
  File "/usr/local/lib/python3.8/dist-packages/mkdocs/config/config_options.py", line 1002, in <genexpr>
    self.warnings.extend(f"Plugin '{name}' value: '{x}'. Warning: {y}" for x, y in warnings)
ValueError: too many values to unpack (expected 2)
```


Discussion:
Uh oh, this is likely a regression in MkDocs 1.4.1

For now please try to install `mkdocs==1.4.0` and let me know if that works
It would be really helpful to be able to reproduce this issue myself and see which plugin is causing this. Would you be able to share your mkdocs.yml ?
Yes of course, here it is:

```yaml
site_name: General Bikeshare Feed Specification
site_url: https://gbfs.mobilitydata.org
repo_url: https://github.com/nabsa/gbfs

theme:
  logo_light_mode: assets/venn.svg
  logo_dark_mode: assets/library-white.svg
  palette:
    - media: "(prefers-color-scheme: light)"
      scheme: default
      primary: blue
      accent: blue
  name: material
  custom_dir: overrides
  favicon: assets/venn-black.svg
  font:
    text: SF Pro
    code: SF Mono
  features:
     - navigation.tabs
     - navigation.indexes
     - navigation.top
     - toc.integrate
  custom_dir: theme_overrides
extra_css:
  - assets/stylesheets/extra.css
extra:
  analytics:
    provider: google
    property: XXXXXXXXXX
  social:
    - icon: fontawesome/brands/github
      link: https://github.com/MobilityData
    - icon: fontawesome/brands/linkedin
      link: https://www.linkedin.com/company/mobilitydata
    - icon: fontawesome/brands/slack
      link: https://bit.ly/mobilitydata-slack
markdown_extensions:
  - attr_list
  - md_in_html
  - admonition
  - toc:
      permalink: true
  - pymdownx.emoji:
      emoji_index: !!python/name:materialx.emoji.twemoji
      emoji_generator: !!python/name:materialx.emoji.to_svg
  - pymdownx.tasklist:
      custom_checkbox: true
  - pymdownx.superfences

plugins:
  - search
  - redirects:
      redirect_maps:
        'home.md': 'index.md'
        'major-change-proposals.md': 'specification/change-proposals.md'
        'tools-services.md': 'toolbox/index.md'
        'faq.md': 'learn/faq.md'
        'about.md': 'participate.md'
  - mkdocs-simple-hooks:
      hooks:
        on_config: "hooks.on_config:deploy_hooks"

nav:
- Home: index.md
- Specification:
  - specification/index.md
  - Current Version: specification/reference.md
  - Change Proposals: specification/change-proposals.md
  - Governance Process: specification/process.md
- Toolbox: 
  - toolbox/index.md
  - Community Tools and Resources: toolbox/resources.md
  - Glossary: toolbox/glossary.md
- Learn:
  - learn/index.md
  - White Papers: 
    - learn/white-papers/index.md
    - Data Policy: learn/white-papers/data-policy.md
    - Data Policy Europe: learn/white-papers/data-policy-europe.md
  - FAQ: learn/faq.md
- Participate: participate.md
```
Holy molly, reverting to 1.4.0 has fixed the issue :-O

I'm in no way an expert in MkDocs or python, so I wasn't aware of possible issues like this, I was upgrading left and right whatever I could find :-) 
By the way this is public and open source documentation, you can recreate the entire site yourself from this repo: https://github.com/MobilityData/gbfs.mobilitydata.org

Resolving patch:
diff --git a/mkdocs/config/base.py b/mkdocs/config/base.py
--- a/mkdocs/config/base.py
+++ b/mkdocs/config/base.py
@@ -363,13 +363,13 @@ def load_config(config_file: Optional[Union[str, IO]] = None, **kwargs) -> MkDoc
     errors, warnings = cfg.validate()
 
     for config_name, warning in warnings:
-        log.warning(f"Config value: '{config_name}'. Warning: {warning}")
+        log.warning(f"Config value '{config_name}': {warning}")
 
     for config_name, error in errors:
-        log.error(f"Config value: '{config_name}'. Error: {error}")
+        log.error(f"Config value '{config_name}': {error}")
 
     for key, value in cfg.items():
-        log.debug(f"Config value: '{key}' = {value!r}")
+        log.debug(f"Config value '{key}' = {value!r}")
 
     if len(errors) > 0:
         raise exceptions.Abort(f"Aborted with {len(errors)} Configuration Errors!")
diff --git a/mkdocs/config/config_options.py b/mkdocs/config/config_options.py
--- a/mkdocs/config/config_options.py
+++ b/mkdocs/config/config_options.py
@@ -96,11 +96,11 @@ def run_validation(self, value: object) -> SomeConfig:
 
         if self._do_validation:
             # Capture errors and warnings
-            self.warnings = [f'Sub-option {key!r}: {msg}' for key, msg in warnings]
+            self.warnings = [f"Sub-option '{key}': {msg}" for key, msg in warnings]
             if failed:
                 # Get the first failing one
                 key, err = failed[0]
-                raise ValidationError(f"Sub-option {key!r} configuration error: {err}")
+                raise ValidationError(f"Sub-option '{key}': {err}")
 
         return config
 
@@ -309,7 +309,7 @@ def __init__(
             else:
                 message = (
                     "The configuration option '{}' has been deprecated and "
-                    "will be removed in a future release of MkDocs."
+                    "will be removed in a future release."
                 )
             if moved_to:
                 message += f" Use '{moved_to}' instead."
@@ -996,11 +996,17 @@ def load_plugin(self, name: str, config) -> plugins.BasePlugin:
             if hasattr(plugin, 'on_startup') or hasattr(plugin, 'on_shutdown'):
                 self.plugin_cache[name] = plugin
 
-        errors, warnings = plugin.load_config(
+        errors, warns = plugin.load_config(
             config, self._config.config_file_path if self._config else None
         )
-        self.warnings.extend(f"Plugin '{name}' value: '{x}'. Warning: {y}" for x, y in warnings)
-        errors_message = '\n'.join(f"Plugin '{name}' value: '{x}'. Error: {y}" for x, y in errors)
+        for warning in warns:
+            if isinstance(warning, str):
+                self.warnings.append(f"Plugin '{name}': {warning}")
+            else:
+                key, msg = warning
+                self.warnings.append(f"Plugin '{name}' option '{key}': {msg}")
+
+        errors_message = '\n'.join(f"Plugin '{name}' option '{key}': {msg}" for key, msg in errors)
         if errors_message:
             raise ValidationError(errors_message)
         return plugin


Test patch:
diff --git a/mkdocs/tests/config/base_tests.py b/mkdocs/tests/config/base_tests.py
--- a/mkdocs/tests/config/base_tests.py
+++ b/mkdocs/tests/config/base_tests.py
@@ -147,7 +147,7 @@ def test_load_missing_required(self, temp_dir):
                 base.load_config(config_file=config_file.name)
         self.assertEqual(
             '\n'.join(cm.output),
-            "ERROR:mkdocs.config:Config value: 'site_name'. Error: Required configuration not provided.",
+            "ERROR:mkdocs.config:Config value 'site_name': Required configuration not provided.",
         )
 
     def test_pre_validation_error(self):
diff --git a/mkdocs/tests/config/config_options_legacy_tests.py b/mkdocs/tests/config/config_options_legacy_tests.py
--- a/mkdocs/tests/config/config_options_legacy_tests.py
+++ b/mkdocs/tests/config/config_options_legacy_tests.py
@@ -190,7 +190,7 @@ class Schema:
             {'d': 'value'},
             warnings=dict(
                 d="The configuration option 'd' has been deprecated and will be removed in a "
-                "future release of MkDocs."
+                "future release."
             ),
         )
 
@@ -209,7 +209,7 @@ class Schema:
             {'d': 'value'},
             warnings=dict(
                 d="The configuration option 'd' has been deprecated and will be removed in a "
-                "future release of MkDocs."
+                "future release."
             ),
         )
 
@@ -223,7 +223,7 @@ class Schema:
                 {'d': 'value'},
                 warnings=dict(
                     d="The configuration option 'd' has been deprecated and will be removed in a "
-                    "future release of MkDocs."
+                    "future release."
                 ),
             )
 
@@ -252,7 +252,7 @@ class Schema:
             {'old': 'value'},
             warnings=dict(
                 old="The configuration option 'old' has been deprecated and will be removed in a "
-                "future release of MkDocs. Use 'new' instead."
+                "future release. Use 'new' instead."
             ),
         )
         self.assertEqual(conf, {'new': 'value', 'old': None})
@@ -267,7 +267,7 @@ class Schema:
             {'old': 'value'},
             warnings=dict(
                 old="The configuration option 'old' has been deprecated and will be removed in a "
-                "future release of MkDocs. Use 'foo.bar' instead."
+                "future release. Use 'foo.bar' instead."
             ),
         )
         self.assertEqual(conf, {'foo': {'bar': 'value'}, 'old': None})
@@ -282,7 +282,7 @@ class Schema:
             {'old': 'value', 'foo': {'existing': 'existing'}},
             warnings=dict(
                 old="The configuration option 'old' has been deprecated and will be removed in a "
-                "future release of MkDocs. Use 'foo.bar' instead."
+                "future release. Use 'foo.bar' instead."
             ),
         )
         self.assertEqual(conf, {'foo': {'existing': 'existing', 'bar': 'value'}, 'old': None})
@@ -298,7 +298,7 @@ class Schema:
                 {'old': 'value', 'foo': 'wrong type'},
                 warnings=dict(
                     old="The configuration option 'old' has been deprecated and will be removed in a "
-                    "future release of MkDocs. Use 'foo.bar' instead."
+                    "future release. Use 'foo.bar' instead."
                 ),
             )
 
@@ -1247,7 +1247,7 @@ class Schema:
             )
 
         with self.expect_error(
-            option="Sub-option 'cc' configuration error: Expected one of: ('foo', 'bar') but received: True"
+            option="Sub-option 'cc': Expected one of: ('foo', 'bar') but received: True"
         ):
             self.get_config(Schema, {'option': {'cc': True}})
 
@@ -1320,7 +1320,7 @@ class Schema:
             conf = self.get_config(Schema, {'sub': None})
 
         with self.expect_error(
-            sub="Sub-option 'opt' configuration error: Expected type: <class 'int'> but received: <class 'str'>"
+            sub="Sub-option 'opt': Expected type: <class 'int'> but received: <class 'str'>"
         ):
             conf = self.get_config(Schema, {'sub': [{'opt': 'asdf'}, {}]})
 
@@ -1330,14 +1330,12 @@ class Schema:
         self.assertEqual(conf['sub'], [{'opt': 1}, {'opt': 2}])
 
         with self.expect_error(
-            sub="Sub-option 'opt' configuration error: Expected type: <class 'int'> but "
-            "received: <class 'str'>"
+            sub="Sub-option 'opt': Expected type: <class 'int'> but received: <class 'str'>"
         ):
             self.get_config(Schema, {'sub': [{'opt': 'z'}, {'opt': 2}]})
 
         with self.expect_error(
-            sub="Sub-option 'opt' configuration error: "
-            "Expected type: <class 'int'> but received: <class 'str'>"
+            sub="Sub-option 'opt': Expected type: <class 'int'> but received: <class 'str'>"
         ):
             conf = self.get_config(Schema, {'sub': [{'opt': 'z'}, {'opt': 2}]})
 
diff --git a/mkdocs/tests/config/config_options_tests.py b/mkdocs/tests/config/config_options_tests.py
--- a/mkdocs/tests/config/config_options_tests.py
+++ b/mkdocs/tests/config/config_options_tests.py
@@ -171,7 +171,7 @@ class Schema(Config):
             {'d': 'value'},
             warnings=dict(
                 d="The configuration option 'd' has been deprecated and will be removed in a "
-                "future release of MkDocs."
+                "future release."
             ),
         )
 
@@ -190,7 +190,7 @@ class Schema(Config):
             {'d': 'value'},
             warnings=dict(
                 d="The configuration option 'd' has been deprecated and will be removed in a "
-                "future release of MkDocs."
+                "future release."
             ),
         )
 
@@ -204,7 +204,7 @@ class Schema(Config):
                 {'d': 'value'},
                 warnings=dict(
                     d="The configuration option 'd' has been deprecated and will be removed in a "
-                    "future release of MkDocs."
+                    "future release."
                 ),
             )
 
@@ -233,7 +233,7 @@ class Schema(Config):
             {'old': 'value'},
             warnings=dict(
                 old="The configuration option 'old' has been deprecated and will be removed in a "
-                "future release of MkDocs. Use 'new' instead."
+                "future release. Use 'new' instead."
             ),
         )
         self.assertEqual(conf, {'new': 'value', 'old': None})
@@ -248,7 +248,7 @@ class Schema(Config):
             {'old': 'value'},
             warnings=dict(
                 old="The configuration option 'old' has been deprecated and will be removed in a "
-                "future release of MkDocs. Use 'foo.bar' instead."
+                "future release. Use 'foo.bar' instead."
             ),
         )
         self.assertEqual(conf, {'foo': {'bar': 'value'}, 'old': None})
@@ -263,7 +263,7 @@ class Schema(Config):
             {'old': 'value', 'foo': {'existing': 'existing'}},
             warnings=dict(
                 old="The configuration option 'old' has been deprecated and will be removed in a "
-                "future release of MkDocs. Use 'foo.bar' instead."
+                "future release. Use 'foo.bar' instead."
             ),
         )
         self.assertEqual(conf, {'foo': {'existing': 'existing', 'bar': 'value'}, 'old': None})
@@ -279,7 +279,7 @@ class Schema(Config):
                 {'old': 'value', 'foo': 'wrong type'},
                 warnings=dict(
                     old="The configuration option 'old' has been deprecated and will be removed in a "
-                    "future release of MkDocs. Use 'foo.bar' instead."
+                    "future release. Use 'foo.bar' instead."
                 ),
             )
 
@@ -1252,7 +1252,7 @@ class Schema(Config):
             option = c.SubConfig(Sub)
 
         with self.expect_error(
-            option="Sub-option 'cc' configuration error: Expected one of: ('foo', 'bar') but received: True"
+            option="Sub-option 'cc': Expected one of: ('foo', 'bar') but received: True"
         ):
             self.get_config(Schema, {'option': {'cc': True}})
 
@@ -1330,7 +1330,7 @@ class Schema(Config):
             conf = self.get_config(Schema, {'sub': None})
 
         with self.expect_error(
-            sub="Sub-option 'opt' configuration error: Expected type: <class 'int'> but received: <class 'str'>"
+            sub="Sub-option 'opt': Expected type: <class 'int'> but received: <class 'str'>"
         ):
             conf = self.get_config(Schema, {'sub': [{'opt': 'asdf'}, {}]})
 
@@ -1343,14 +1343,12 @@ class Schema(Config):
         self.assertEqual(conf.sub[0].opt, 1)
 
         with self.expect_error(
-            sub="Sub-option 'opt' configuration error: Expected type: <class 'int'> but "
-            "received: <class 'str'>"
+            sub="Sub-option 'opt': Expected type: <class 'int'> but received: <class 'str'>"
         ):
             self.get_config(Schema, {'sub': [{'opt': 'z'}, {'opt': 2}]})
 
         with self.expect_error(
-            sub="Sub-option 'opt' configuration error: "
-            "Expected type: <class 'int'> but received: <class 'str'>"
+            sub="Sub-option 'opt': Expected type: <class 'int'> but received: <class 'str'>"
         ):
             conf = self.get_config(Schema, {'sub': [{'opt': 'z'}, {'opt': 2}]})
 
@@ -1927,7 +1925,7 @@ class Schema(Config):
             }
         }
         with self.expect_error(
-            plugins="Plugin 'sample' value: 'bar'. Error: Expected type: <class 'int'> but received: <class 'str'>"
+            plugins="Plugin 'sample' option 'bar': Expected type: <class 'int'> but received: <class 'str'>"
         ):
             self.get_config(Schema, cfg)
 
@@ -1944,8 +1942,8 @@ class Schema(Config):
             Schema,
             cfg,
             warnings=dict(
-                plugins="Plugin 'sample2' value: 'depr'. Warning: The configuration option "
-                "'depr' has been deprecated and will be removed in a future release of MkDocs."
+                plugins="Plugin 'sample2' option 'depr': The configuration option "
+                "'depr' has been deprecated and will be removed in a future release."
             ),
         )