diff --git a/numpydoc/docscrape.py b/numpydoc/docscrape.py
index 074a7f7..45883d0 100644
--- a/numpydoc/docscrape.py
+++ b/numpydoc/docscrape.py
@@ -136,7 +136,7 @@ class NumpyDocString(collections.Mapping):
 
     def __setitem__(self, key, val):
         if key not in self._parsed_data:
-            warn("Unknown section %s" % key)
+            self._error_location("Unknown section %s" % key, error=False)
         else:
             self._parsed_data[key] = val
 
@@ -331,19 +331,8 @@ class NumpyDocString(collections.Mapping):
                 section = (s.capitalize() for s in section.split(' '))
                 section = ' '.join(section)
                 if self.get(section):
-                    if hasattr(self, '_obj'):
-                        # we know where the docs came from:
-                        try:
-                            filename = inspect.getsourcefile(self._obj)
-                        except TypeError:
-                            filename = None
-                        msg = ("The section %s appears twice in "
-                               "the docstring of %s in %s." %
-                               (section, self._obj, filename))
-                        raise ValueError(msg)
-                    else:
-                        msg = ("The section %s appears twice" % section)
-                        raise ValueError(msg)
+                    self._error_location("The section %s appears twice"
+                                         % section)
 
             if section in ('Parameters', 'Returns', 'Yields', 'Raises',
                            'Warns', 'Other Parameters', 'Attributes',
@@ -356,6 +345,20 @@ class NumpyDocString(collections.Mapping):
             else:
                 self[section] = content
 
+    def _error_location(self, msg, error=True):
+        if hasattr(self, '_obj'):
+            # we know where the docs came from:
+            try:
+                filename = inspect.getsourcefile(self._obj)
+            except TypeError:
+                filename = None
+            msg = msg + (" in the docstring of %s in %s."
+                         % (self._obj, filename))
+        if error:
+            raise ValueError(msg)
+        else:
+            warn(msg)
+
     # string conversion routines
 
     def _str_header(self, name, symbol='-'):

Unknown Section error has no context.
Similar to #95 the "Unknown section: X" error has no context, making it really hard to find and fix :-/
diff --git a/numpydoc/tests/test_docscrape.py b/numpydoc/tests/test_docscrape.py
index 297a0ac..2dc45e3 100644
--- a/numpydoc/tests/test_docscrape.py
+++ b/numpydoc/tests/test_docscrape.py
@@ -3,6 +3,7 @@ from __future__ import division, absolute_import, print_function
 
 import sys
 import textwrap
+import warnings
 
 import jinja2
 
@@ -151,13 +152,16 @@ def test_signature():
     assert doc['Signature'].startswith('numpy.multivariate_normal(')
     assert doc['Signature'].endswith('spam=None)')
 
+
 def test_summary():
     assert doc['Summary'][0].startswith('Draw values')
     assert doc['Summary'][-1].endswith('covariance.')
 
+
 def test_extended_summary():
     assert doc['Extended Summary'][0].startswith('The multivariate normal')
 
+
 def test_parameters():
     assert_equal(len(doc['Parameters']), 3)
     assert_equal([n for n,_,_ in doc['Parameters']], ['mean','cov','shape'])
@@ -167,6 +171,7 @@ def test_parameters():
     assert desc[0].startswith('Covariance matrix')
     assert doc['Parameters'][0][-1][-2] == '   (1+2+3)/3'
 
+
 def test_other_parameters():
     assert_equal(len(doc['Other Parameters']), 1)
     assert_equal([n for n,_,_ in doc['Other Parameters']], ['spam'])
@@ -174,6 +179,7 @@ def test_other_parameters():
     assert_equal(arg_type, 'parrot')
     assert desc[0].startswith('A parrot off its mortal coil')
 
+
 def test_returns():
     assert_equal(len(doc['Returns']), 2)
     arg, arg_type, desc = doc['Returns'][0]
@@ -188,6 +194,7 @@ def test_returns():
     assert desc[0].startswith('This is not a real')
     assert desc[-1].endswith('anonymous return values.')
 
+
 def test_yields():
     section = doc_yields['Yields']
     assert_equal(len(section), 3)
@@ -200,6 +207,7 @@ def test_yields():
         assert desc[0].startswith('The number of')
         assert desc[0].endswith(end)
 
+
 def test_returnyield():
     doc_text = """
 Test having returns and yields.
@@ -289,26 +297,31 @@ def test_notes():
     assert doc['Notes'][-1].endswith('definite.')
     assert_equal(len(doc['Notes']), 17)
 
+
 def test_references():
     assert doc['References'][0].startswith('..')
     assert doc['References'][-1].endswith('2001.')
 
+
 def test_examples():
     assert doc['Examples'][0].startswith('>>>')
     assert doc['Examples'][-1].endswith('True]')
 
+
 def test_index():
     assert_equal(doc['index']['default'], 'random')
     assert_equal(len(doc['index']), 2)
     assert_equal(len(doc['index']['refguide']), 2)
 
-def non_blank_line_by_line_compare(a,b):
+
+def non_blank_line_by_line_compare(a, b):
     a = textwrap.dedent(a)
     b = textwrap.dedent(b)
     a = [l.rstrip() for l in a.split('\n') if l.strip()]
     b = [l.rstrip() for l in b.split('\n') if l.strip()]
     assert_list_equal(a, b)
 
+
 def test_str():
     # doc_txt has the order of Notes and See Also sections flipped.
     # This should be handled automatically, and so, one thing this test does
@@ -595,15 +608,18 @@ doc2 = NumpyDocString("""
         If None, the index is into the flattened array, otherwise along
         the specified axis""")
 
+
 def test_parameters_without_extended_description():
     assert_equal(len(doc2['Parameters']), 2)
 
+
 doc3 = NumpyDocString("""
     my_signature(*params, **kwds)
 
     Return this and that.
     """)
 
+
 def test_escape_stars():
     signature = str(doc3).split('\n')[0]
     assert_equal(signature, 'my_signature(\*params, \*\*kwds)')
@@ -614,14 +630,17 @@ def test_escape_stars():
     fdoc = FunctionDoc(func=my_func)
     assert_equal(fdoc['Signature'], 'my_func(a, b, \*\*kwargs)')
 
+
 doc4 = NumpyDocString(
     """a.conj()
 
     Return an array with all complex-valued elements conjugated.""")
 
+
 def test_empty_extended_summary():
     assert_equal(doc4['Extended Summary'], [])
 
+
 doc5 = NumpyDocString(
     """
     a.something()
@@ -637,18 +656,21 @@ doc5 = NumpyDocString(
         If needed
     """)
 
+
 def test_raises():
     assert_equal(len(doc5['Raises']), 1)
     name,_,desc = doc5['Raises'][0]
     assert_equal(name,'LinAlgException')
     assert_equal(desc,['If array is singular.'])
 
+
 def test_warns():
     assert_equal(len(doc5['Warns']), 1)
     name,_,desc = doc5['Warns'][0]
     assert_equal(name,'SomeWarning')
     assert_equal(desc,['If needed'])
 
+
 def test_see_also():
     doc6 = NumpyDocString(
     """
@@ -726,12 +748,45 @@ def test_see_also_print():
     assert('    some relationship' in s)
     assert(':func:`func_d`' in s)
 
+
+def test_unknown_section():
+    doc_text = """
+Test having an unknown section
+
+Mope
+----
+This should be ignored and warned about
+"""
+
+    class BadSection(object):
+        """Class with bad section.
+
+        Nope
+        ----
+        This class has a nope section.
+        """
+        pass
+
+    with warnings.catch_warnings(record=True) as w:
+        NumpyDocString(doc_text)
+        assert len(w) == 1
+        assert "Unknown section Mope" == str(w[0].message)
+
+    with warnings.catch_warnings(record=True) as w:
+        SphinxClassDoc(BadSection)
+        assert len(w) == 1
+        assert_true('test_docscrape.test_unknown_section.<locals>.BadSection'
+                    in str(w[0].message)
+                    or 'test_docscrape.BadSection' in str(w[0].message))
+
+
 doc7 = NumpyDocString("""
 
         Doc starts on second line.
 
         """)
 
+
 def test_empty_first_line():
     assert doc7['Summary'][0].startswith('Doc starts')
 
@@ -762,6 +817,7 @@ def test_unicode():
     assert isinstance(doc['Summary'][0], str)
     assert doc['Summary'][0] == 'öäöäöäöäöåååå'
 
+
 def test_plot_examples():
     cfg = dict(use_plots=True)
 
@@ -785,6 +841,7 @@ def test_plot_examples():
     """, config=cfg)
     assert str(doc).count('plot::') == 1, str(doc)
 
+
 def test_class_members():
 
     class Dummy(object):
@@ -866,6 +923,7 @@ def test_class_members():
         else:
             assert 'Spammity index' in str(doc), str(doc)
 
+
 def test_duplicate_signature():
     # Duplicate function signatures occur e.g. in ufuncs, when the
     # automatic mechanism adds one, and a more detailed comes from the
@@ -911,6 +969,7 @@ class_doc_txt = """
     For usage examples, see `ode`.
 """
 
+
 def test_class_members_doc():
     doc = ClassDoc(None, class_doc_txt)
     non_blank_line_by_line_compare(str(doc),
@@ -949,6 +1008,7 @@ def test_class_members_doc():
 
     """)
 
+
 def test_class_members_doc_sphinx():
     class Foo:
         @property
@@ -997,6 +1057,7 @@ def test_class_members_doc_sphinx():
 
     """)
 
+
 def test_templated_sections():
     doc = SphinxClassDoc(None, class_doc_txt,
                          config={'template': jinja2.Template('{{examples}}{{parameters}}')})
@@ -1020,8 +1081,6 @@ def test_templated_sections():
     """)
 
 
-
-
 if __name__ == "__main__":
     import nose
     nose.run()

pip install -e .
parse_log_pytest
requirements.txt
nose pytest
apt-get update apt-get install -y gcc
3.6
requirements/base.txt
pytest --no-header -rA --tb=line --color=no -p no:cacheprovider -W ignore::DeprecationWarning
alabaster==0.7.13
attrs==22.2.0
Babel==2.11.0
certifi==2021.5.30
charset-normalizer==2.0.12
docutils==0.18.1
idna==3.10
imagesize==1.4.1
importlib-metadata==4.8.3
iniconfig==1.1.1
Jinja2==3.0.3
MarkupSafe==2.0.1
nose==1.3.7
-e git+https://github.com/numpy/numpydoc.git@057ef572dd5ea547af2832e1b68d934cf953eafe#egg=numpydoc
packaging==21.3
pluggy==1.0.0
py==1.11.0
Pygments==2.14.0
pyparsing==3.1.4
pytest==7.0.1
pytz==2025.2
requests==2.27.1
snowballstemmer==2.2.0
Sphinx==5.3.0
sphinxcontrib-applehelp==1.0.2
sphinxcontrib-devhelp==1.0.2
sphinxcontrib-htmlhelp==2.0.0
sphinxcontrib-jsmath==1.0.1
sphinxcontrib-qthelp==1.0.3
sphinxcontrib-serializinghtml==1.1.5
tomli==1.2.3
typing_extensions==4.1.1
urllib3==1.26.20
zipp==3.6.0

name: numpydoc
channels:
  - defaults
  - https://repo.anaconda.com/pkgs/main
  - https://repo.anaconda.com/pkgs/r
  - conda-forge
dependencies:
  - _libgcc_mutex=0.1=main
  - _openmp_mutex=5.1=1_gnu
  - ca-certificates=2025.2.25=h06a4308_0
  - certifi=2021.5.30=py36h06a4308_0
  - ld_impl_linux-64=2.40=h12ee557_0
  - libffi=3.3=he6710b0_2
  - libgcc-ng=11.2.0=h1234567_1
  - libgomp=11.2.0=h1234567_1
  - libstdcxx-ng=11.2.0=h1234567_1
  - ncurses=6.4=h6a678d5_0
  - openssl=1.1.1w=h7f8727e_0
  - pip=21.2.2=py36h06a4308_0
  - python=3.6.13=h12debd9_1
  - readline=8.2=h5eee18b_0
  - setuptools=58.0.4=py36h06a4308_0
  - sqlite=3.45.3=h5eee18b_0
  - tk=8.6.14=h39e8969_0
  - wheel=0.37.1=pyhd3eb1b0_0
  - xz=5.6.4=h5eee18b_1
  - zlib=1.2.13=h5eee18b_1
  - pip:
      - alabaster==0.7.13
      - attrs==22.2.0
      - babel==2.11.0
      - charset-normalizer==2.0.12
      - docutils==0.18.1
      - idna==3.10
      - imagesize==1.4.1
      - importlib-metadata==4.8.3
      - iniconfig==1.1.1
      - jinja2==3.0.3
      - markupsafe==2.0.1
      - nose==1.3.7
      - packaging==21.3
      - pluggy==1.0.0
      - py==1.11.0
      - pygments==2.14.0
      - pyparsing==3.1.4
      - pytest==7.0.1
      - pytz==2025.2
      - requests==2.27.1
      - snowballstemmer==2.2.0
      - sphinx==5.3.0
      - sphinxcontrib-applehelp==1.0.2
      - sphinxcontrib-devhelp==1.0.2
      - sphinxcontrib-htmlhelp==2.0.0
      - sphinxcontrib-jsmath==1.0.1
      - sphinxcontrib-qthelp==1.0.3
      - sphinxcontrib-serializinghtml==1.1.5
      - tomli==1.2.3
      - typing-extensions==4.1.1
      - urllib3==1.26.20
      - zipp==3.6.0
prefix: /opt/conda/envs/numpydoc

numpydoc/tests/test_docscrape.py::test_unknown_section
numpydoc/tests/test_docscrape.py::test_signature numpydoc/tests/test_docscrape.py::test_summary numpydoc/tests/test_docscrape.py::test_extended_summary numpydoc/tests/test_docscrape.py::test_parameters numpydoc/tests/test_docscrape.py::test_other_parameters numpydoc/tests/test_docscrape.py::test_returns numpydoc/tests/test_docscrape.py::test_yields numpydoc/tests/test_docscrape.py::test_returnyield numpydoc/tests/test_docscrape.py::test_section_twice numpydoc/tests/test_docscrape.py::test_notes numpydoc/tests/test_docscrape.py::test_references numpydoc/tests/test_docscrape.py::test_examples numpydoc/tests/test_docscrape.py::test_index numpydoc/tests/test_docscrape.py::test_str numpydoc/tests/test_docscrape.py::test_yield_str numpydoc/tests/test_docscrape.py::test_sphinx_str numpydoc/tests/test_docscrape.py::test_sphinx_yields_str numpydoc/tests/test_docscrape.py::test_parameters_without_extended_description numpydoc/tests/test_docscrape.py::test_escape_stars numpydoc/tests/test_docscrape.py::test_empty_extended_summary numpydoc/tests/test_docscrape.py::test_raises numpydoc/tests/test_docscrape.py::test_warns numpydoc/tests/test_docscrape.py::test_see_also numpydoc/tests/test_docscrape.py::test_see_also_parse_error numpydoc/tests/test_docscrape.py::test_see_also_print numpydoc/tests/test_docscrape.py::test_empty_first_line numpydoc/tests/test_docscrape.py::test_no_summary numpydoc/tests/test_docscrape.py::test_unicode numpydoc/tests/test_docscrape.py::test_plot_examples numpydoc/tests/test_docscrape.py::test_class_members numpydoc/tests/test_docscrape.py::test_duplicate_signature numpydoc/tests/test_docscrape.py::test_class_members_doc numpydoc/tests/test_docscrape.py::test_class_members_doc_sphinx numpydoc/tests/test_docscrape.py::test_templated_sections
numpydoc/docscrape.py
numpydoc/docscrape.py