Skip to content

Fix Brandl2020 downloading HTML error pages instead of data - #1144

Open
bruAristimunha wants to merge 2 commits into
developfrom
fix-brandl2020-download
Open

Fix Brandl2020 downloading HTML error pages instead of data#1144
bruAristimunha wants to merge 2 commits into
developfrom
fix-brandl2020-download

Conversation

@bruAristimunha

Copy link
Copy Markdown
Collaborator

Fixes #1141.

The bug

Brandl2020 downloaded 16 subject files and the montage as byte-identical 1306-byte HTML pages, and nothing raised. DepositOnce migrated to DSpace 7, and its web host now answers every path — including ones that used to be files — with HTTP 200 and the Angular app shell instead of a 404:

$ curl -sL -o /dev/null -w '%{http_code} %{size_download}B %{content_type}\n' \
    https://depositonce.tu-berlin.de/bitstreams/handle/11303/10934.2/pp2.mat
200 1306B text/html; charset=utf-8

Because the status is 200, pooch cached the error body under the .mat name and recorded a successful download. Every later call was then served that cache without touching the network, so the failure only surfaced when something tried to read the file.

The fix

The REST API is on a different host. That is why probing depositonce.tu-berlin.de looks like a total outage — even /server/api returns the shell. The front end names its own API location in assets/config.json:

$ curl -s https://depositonce.tu-berlin.de/assets/config.<hash>.json | jq .rest.baseUrl
"https://api-depositonce.tu-berlin.de/server"

That host serves the bytes and honours Range requests. The per-file bitstream UUIDs needed to address it were already in this module but never used_BITSTREAM_UUIDS was dead code while data_path still built URLs against the broken host. This PR wires them up.

And verifies what arrives. Every file here is MATLAB v5 or v7.3, both of which begin with the ASCII banner MATLAB. A payload that does not is deleted and raises with an explanation. A cached bad file is worse than a failed download — it fails at analysis time, far from the cause.

data_dl gains an optional fname, because DSpace serves every bitstream from .../bitstreams/<uuid>/content; without it all 17 files would be stored as content.

Verification

I downloaded the full distribution through this route: 19 files, 10.59 GiB, each checked against the size and checksum the API reports, all valid MATLAB.

mnt.mat 1285 B · bands.mat 256 B · ivals.mat 256 B · README.md 3321 B
pp1.mat … pp16.mat   (615–802 MiB each)

New tests (parametrized) cover: the URL targets the API host with the right UUID, fname is passed so files are not all named content, an HTML/empty/garbage payload raises and is removed from the cache, and an unknown filename is rejected.

pytest moabb/tests/test_download.py moabb/tests/test_datasets.py passes; ruff check and ruff format are clean.

Note for maintainers

Two things I deliberately left out of this PR:

  1. bands.mat and ivals.mat ship with the distribution but are not fetched by this module. I did not add them, since nothing in the loader uses them — flagging in case that is unintentional.
  2. The guard here is dataset-local. A content-type check inside data_dl would protect every dataset against this class of failure, but it risks breaking downloads I cannot test, so it felt wrong to bundle with a bug fix. Happy to send it separately if you want it.

DepositOnce migrated to DSpace 7. Its web host now answers every path --
including ones that used to be files -- with HTTP 200 and a 1306-byte
Angular app shell rather than a 404, so pooch stored that HTML under each
requested .mat name and recorded a successful download. All 16 subjects
and the montage cached as byte-identical HTML, and nothing raised.

The REST API is on a different host, api-depositonce.tu-berlin.de, named
in the front end's own assets/config.json. It serves the bytes and honours
Range requests. The per-file bitstream UUIDs needed to address it were
already in this module but unused; the download path still built URLs
against the broken host.

Two changes:

- Point downloads at the REST host, addressed by those UUIDs.
- Verify what arrives. Every file here is MATLAB v5 or v7.3, both of which
  begin with the ASCII banner "MATLAB". A payload that does not is deleted
  and raises, because a cached bad file is worse than a failed download:
  every later call is served the same bytes without touching the network,
  so the failure surfaces at analysis time instead of at fetch time.

data_dl gains an optional fname, since DSpace serves every bitstream from
.../bitstreams/<uuid>/content and URL-derived naming would store them all
as "content".
Review of the first commit raised three things:

A bare RuntimeError is indistinguishable from the ones moabb raises for a
missing unrar binary or an absent XDF stream, and base.py catches
(RuntimeError, ValueError, OSError) broadly -- so a benchmark sweeping
datasets could not tell "DepositOnce is down, skip it" from "moabb has a
bug". Adds DatasetDownloadError beside the existing NemarDownloadError,
for a download that succeeded but did not deliver the dataset's data.

A stale bitstream UUID answers 404 with JSON, not 200 with HTML, so the
payload check never sees it and the user gets a bare HTTPError naming an
opaque UUID that appears nowhere in the dataset. That case is now caught
and re-raised pointing at the stable handle, with the resolution command
recorded next to the table it regenerates.

The old message told the user to "retry later" -- an infinite loop for a
permanent failure. It now says the failure is not transient.

The URL test asserted the module formats its own constants, which cannot
fail for the reason the table exists to worry about. Replaced with two
properties that can: the table covers pp1-pp16 plus mnt.mat exactly, and
no UUID repeats (a copy-paste slip would serve one subject's recording
under another's name).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Brandl2020: upstream returns HTTP 200 + HTML for every file, so 16 subjects cache as 1306-byte error pages

1 participant