Skip to content

Issue with error about missing END card #4351

Closed

Description

@astrofrog

The following example raises an error:

In [1]: from astropy.io import fits

In [2]: fits.open('https://dataverse.harvard.edu/api/access/datafile/2439198')
Downloading https://dataverse.harvard.edu/api/access/datafile/2439198
|==================================================================| 511M/511M (100.00%)     14m38s
---------------------------------------------------------------------------
OSError                                   Traceback (most recent call last)
<ipython-input-2-95ceb74571c6> in <module>()
----> 1 fits.open('https://dataverse.harvard.edu/api/access/datafile/2439198')

/Volumes/Raptor/miniconda3/envs/production/lib/python3.4/site-packages/astropy-1.1.dev14143-py3.4-macosx-10.5-x86_64.egg/astropy/io/fits/hdu/hdulist.py in fitsopen(name, mode, memmap, save_backup, cache, **kwargs)
    136         raise ValueError('Empty filename: %s' % repr(name))
    137 
--> 138     return HDUList.fromfile(name, mode, memmap, save_backup, cache, **kwargs)
    139 
    140 

/Volumes/Raptor/miniconda3/envs/production/lib/python3.4/site-packages/astropy-1.1.dev14143-py3.4-macosx-10.5-x86_64.egg/astropy/io/fits/hdu/hdulist.py in fromfile(cls, fileobj, mode, memmap, save_backup, cache, **kwargs)
    278 
    279         return cls._readfrom(fileobj=fileobj, mode=mode, memmap=memmap,
--> 280                              save_backup=save_backup, cache=cache, **kwargs)
    281 
    282     @classmethod

/Volumes/Raptor/miniconda3/envs/production/lib/python3.4/site-packages/astropy-1.1.dev14143-py3.4-macosx-10.5-x86_64.egg/astropy/io/fits/hdu/hdulist.py in _readfrom(cls, fileobj, data, mode, memmap, save_backup, cache, **kwargs)
    836 
    837                         try:
--> 838                             hdu = _BaseHDU.readfrom(ffo, **kwargs)
    839                         except EOFError:
    840                             break

/Volumes/Raptor/miniconda3/envs/production/lib/python3.4/site-packages/astropy-1.1.dev14143-py3.4-macosx-10.5-x86_64.egg/astropy/io/fits/hdu/base.py in readfrom(cls, fileobj, checksum, ignore_missing_end, **kwargs)
    420         hdu = cls._readfrom_internal(fileobj, checksum=checksum,
    421                                      ignore_missing_end=ignore_missing_end,
--> 422                                      **kwargs)
    423 
    424         # If the checksum had to be checked the data may have already been read

/Volumes/Raptor/miniconda3/envs/production/lib/python3.4/site-packages/astropy-1.1.dev14143-py3.4-macosx-10.5-x86_64.egg/astropy/io/fits/hdu/base.py in _readfrom_internal(cls, data, header, checksum, ignore_missing_end, **kwargs)
    480             if header is None:
    481                 header_offset = data.tell()
--> 482                 header = Header.fromfile(data, endcard=not ignore_missing_end)
    483             hdu_fileobj = data
    484             data_offset = data.tell()  # *after* reading the header

/Volumes/Raptor/miniconda3/envs/production/lib/python3.4/site-packages/astropy-1.1.dev14143-py3.4-macosx-10.5-x86_64.egg/astropy/io/fits/header.py in fromfile(cls, fileobj, sep, endcard, padding)
    458 
    459             return cls._from_blocks(block_iter, is_binary, sep, endcard,
--> 460                                     padding)[1]
    461         finally:
    462             if close_file:

/Volumes/Raptor/miniconda3/envs/production/lib/python3.4/site-packages/astropy-1.1.dev14143-py3.4-macosx-10.5-x86_64.egg/astropy/io/fits/header.py in _from_blocks(cls, block_iter, is_binary, sep, endcard, padding)
    527             # TODO: Pass this error to validation framework as an ERROR,
    528             # rather than raising an exception
--> 529             raise IOError('Header missing END card.')
    530 
    531         header_str = ''.join(read_blocks)

OSError: Header missing END card.

(the data is a gzipped 500MB cube)

However, at first glance the FITS file is valid and the header has an END card. What could be going on here? It looks like this fails while trying to read a second HDU (based on preliminary debugging on my side), but there is only one HDU. Could it be that there are more data bytes in the file than the header would suggest and this is causing issues?

cc @embray

Activity

  1. embray commented on Nov 25, 2015

    @embray
    Member

    Probably unexpected trailing bytes #3941

  2. embray commented on Nov 25, 2015

    @embray
    Member

    Yep, that's all it is.

    I want to say this is a known issue (it's very old), but I can't find a specific issue to point to.

  3. embray commented on Nov 25, 2015

    @embray
    Member

    Try fits.open('https://dataverse.harvard.edu/api/access/datafile/2439198', ignore_missing_end=True)

  4. astrofrog commented on Nov 25, 2015

    @astrofrog
    MemberAuthor

    Yes, that works, thanks! And I then see a warning about the padding bytes being present.

  5. embray commented on Nov 25, 2015

    @embray
    Member

    I think, if nothing else, if this is encounted at the end of the file and "extra data" is nothing but zeros it should definitely recognize this as padding and not try to parse it as an extra header. However, if it contains anything but zeros it's harder to just automatically dismiss it as garbage...

  6. astrofrog commented on Nov 25, 2015

    @astrofrog
    MemberAuthor

    I think in the present case, it's just zeros (well, null bytes)

  7. embray commented on Nov 25, 2015

    @embray
    Member

    Yep

  8. SaOgaz commented on Jun 27, 2016

    @SaOgaz
    Contributor

    @classmethod
    def _find_end_card(cls, block, card_len):
    """
    Utility method to search a header block for the END card and handle
    invalid END cards.
    This method can also returned a modified copy of the input header block
    in case an invalid end card needs to be sanitized.
    """

    I don't know if I'm linking this correctly..
    @embray would this be the right method to edit to fix this bug?

  9. SaOgaz commented on Jun 27, 2016

    @SaOgaz
    Contributor

    And maybe work somehow with this line?

    HEADER_END_RE = re.compile(encode_ascii(
    r'(?:(?P<valid>END {77}) *)|(?P<invalid>END$|END {0,76}[^A-Z0-9_-])'))

    goes and studies the re module

  10. embray commented on Jun 29, 2016

    @embray
    Member

    @SaraOgaz I don't think the problem is really with the regular expression for the END card, or finding the END card that's the problem.

    The problem is more that the way HDUs are read is kind of dumb. When reading in a FITS file it goes into this loop (albeit significantly modified in #5065, though the end result is still the same) where as long as there are bytes left to be read in the file, it will try to read those bytes as an HDU. And the first thing that happens when trying to read an HDU is that it tries to read the header.

    The dumb thing is that it just assumes that if there are some remaining bytes in the file, it had better be a valid FITS header. But if there's just trailing padding, or garbage, any attempt to process it as a header will fail.

    I can't tell you off the top of my head (would have to think about it a bit more) exactly where this should be fixed. But I think a special case could be made for a failed attempt to read part of the file as a header if the end of the file is reached.

    E.g. say all the valid HDUs have already been read and processed, but there's something like 20 bytes of invalid padding at the end. Trying to read that as a header will fail with some error or another (the most typical being that it's read in those 20 bytes and failed to find an END card). I think if it's reached the EOF, but failed to find an END card, and, to be extra sure, it does not even look like a valid FITS header (i.e. does not begin with SIMPLE = T, etc.) then ignore any errors about invalid headers and at the most produce a warning that there's trailing garbage at the end of the file.

    There's a special case where this already happens, in fact, and there's a warning message there you can reuse:

    # Strip any zero-padding (see ticket #106)
    The problem is it's just not general enough. It only works if the extra padding is null bytes, but not if it's e.g. spaces, or just random garbage.

  11. pllim commented on Apr 13, 2017

    @pllim
    Member

    Running into the same problem but was using FITSDiff. Any chance this can be a keyword for FITSDiff when user passes in filenames?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions