Repository: pandas-dev/pandas

Issue:
Give more precise warning when passing `header=-1` in `pandas.read_csv`, or don't throw error
#### Code Sample, a copy-pastable example if possible

```python
from io import StringIO
import pandas as pd

print(f"Using pandas version {pd.__version__}")
pd.read_csv(StringIO('spam'), header=-1)
``` 
> Using pandas version 0.25.0
>
>---------------------------------------------------------------------------
>OverflowError  
#### Problem description

In previous versions of pandas (e.g. 0.24.2), this would have worked fine. Instead, now, one has to pass `header=None`. However, when working with old code after having updated pandas, it may be unclear where the problem lies.

#### Expected Output
```
      0
0  spam
```


#### Output of ``pd.show_versions()``

<details>

[paste the output of ``pd.show_versions()`` here below this line]
INSTALLED VERSIONS
------------------
commit           : None
python           : 3.7.3.final.0
python-bits      : 64
OS               : Linux
OS-release       : 4.15.0-55-generic
machine          : x86_64
processor        : x86_64
byteorder        : little
LC_ALL           : None
LANG             : en_GB.UTF-8
LOCALE           : en_GB.UTF-8

pandas           : 0.25.0
numpy            : 1.16.4
pytz             : 2019.1
dateutil         : 2.8.0
pip              : 19.1.1
setuptools       : 41.0.1
Cython           : 0.29.12
pytest           : 4.5.0
hypothesis       : None
sphinx           : None
blosc            : None
feather          : None
xlsxwriter       : None
lxml.etree       : None
html5lib         : None
pymysql          : None
psycopg2         : None
jinja2           : 2.10.1
IPython          : 7.7.0
pandas_datareader: None
bs4              : None
bottleneck       : None
fastparquet      : None
gcsfs            : None
lxml.etree       : None
matplotlib       : 3.1.0
numexpr          : None
odfpy            : None
openpyxl         : None
pandas_gbq       : None
pyarrow          : 0.13.0
pytables         : None
s3fs             : 0.2.1
scipy            : 1.3.0
sqlalchemy       : 1.3.5
tables           : None
xarray           : None
xlrd             : None
xlwt             : None
xlsxwriter       : None

</details>



Discussion:
@MarcoGorelli are you interested in submitting a pull request?
If this would be of interest to you, then yes, sure! I'll get to work

On Fri, 9 Aug 2019, 19:18 Tom Augspurger, <notifications@github.com> wrote:

> @MarcoGorelli <https://github.com/MarcoGorelli> are you interested in
> submitting a pull request?
>
> —
> You are receiving this because you were mentioned.
> Reply to this email directly, view it on GitHub
> <https://github.com/pandas-dev/pandas/issues/27779?email_source=notifications&email_token=AH7QVMG4FEQS62QA4FLRZF3QDWYIFA5CNFSM4IJWWNK2YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOD37NBBI#issuecomment-520016005>,
> or mute the thread
> <https://github.com/notifications/unsubscribe-auth/AH7QVMCECIDPIO55Z5N6TVLQDWYIFANCNFSM4IJWWNKQ>
> .
>


Resolving patch:
diff --git a/pandas/io/parsers.py b/pandas/io/parsers.py
--- a/pandas/io/parsers.py
+++ b/pandas/io/parsers.py
@@ -1393,6 +1393,10 @@ def __init__(self, kwds):
         if isinstance(self.header, (list, tuple, np.ndarray)):
             if not all(map(is_integer, self.header)):
                 raise ValueError("header must be integer or list of integers")
+            if any(i < 0 for i in self.header):
+                raise ValueError(
+                    "cannot specify multi-index header with negative integers"
+                )
             if kwds.get("usecols"):
                 raise ValueError(
                     "cannot specify usecols when specifying a multi-index header"
@@ -1419,6 +1423,13 @@ def __init__(self, kwds):
         elif self.header is not None and not is_integer(self.header):
             raise ValueError("header must be integer or list of integers")
 
+        # GH 27779
+        elif self.header is not None and self.header < 0:
+            raise ValueError(
+                "Passing negative integer to header is invalid. "
+                "For no header, use header=None instead"
+            )
+
         self._name_processed = False
 
         self._first_chunk = True


Test patch:
diff --git a/pandas/tests/io/parser/test_header.py b/pandas/tests/io/parser/test_header.py
--- a/pandas/tests/io/parser/test_header.py
+++ b/pandas/tests/io/parser/test_header.py
@@ -24,6 +24,35 @@ def test_read_with_bad_header(all_parsers):
         parser.read_csv(s, header=[10])
 
 
+def test_negative_header(all_parsers):
+    # see gh-27779
+    parser = all_parsers
+    data = """1,2,3,4,5
+6,7,8,9,10
+11,12,13,14,15
+"""
+    with pytest.raises(
+        ValueError,
+        match="Passing negative integer to header is invalid. "
+        "For no header, use header=None instead",
+    ):
+        parser.read_csv(StringIO(data), header=-1)
+
+
+@pytest.mark.parametrize("header", [([-1, 2, 4]), ([-5, 0])])
+def test_negative_multi_index_header(all_parsers, header):
+    # see gh-27779
+    parser = all_parsers
+    data = """1,2,3,4,5
+        6,7,8,9,10
+        11,12,13,14,15
+        """
+    with pytest.raises(
+        ValueError, match="cannot specify multi-index header with negative integers"
+    ):
+        parser.read_csv(StringIO(data), header=header)
+
+
 @pytest.mark.parametrize("header", [True, False])
 def test_bool_header_arg(all_parsers, header):
     # see gh-6114