Skip to content

Support open-ended ranges in getInputStream, fail loudly on unreadable zip archives - #103

Merged
normanrz merged 2 commits into
mainfrom
claude/issues-100-101-19e045
Sep 9, 2026
Merged

Support open-ended ranges in getInputStream, fail loudly on unreadable zip archives#103
normanrz merged 2 commits into
mainfrom
claude/issues-100-101-19e045

Conversation

@normanrz

@normanrz normanrz commented Sep 9, 2026

Copy link
Copy Markdown
Member

Fixes #100 and #101.

HttpStore.getInputStream and open-ended ranges (#100)

getInputStream(keys, start, end) formatted bytes=%d-%d unconditionally. Since Store.getInputStream(keys) delegates to getInputStream(keys, 0, -1), that became Range: bytes=0--1 — which no server answers, so the method returned null. It now emits bytes=<start>- when end is negative, mirroring what get(keys, start) already did.

S3Store.getInputStream built the same range string and had the identical bug, so it is fixed the same way (and now validates a non-negative start, matching the other stores).

ReadOnlyZipStore silently reporting an unreadable archive as empty (#101)

ensureCache() treated a null stream from the underlying store as a successfully-parsed empty archive, so a read failure surfaced as valid-but-empty data — concretely, a misleading No Zarr group found at … several layers from the cause. It now throws StoreException.readFailed(...), as it already did for an IOException while parsing the ZIP directory. A genuinely empty archive still parses to an empty index through the normal path, so the two cases stay distinguishable.

Tests

  • StoreTest.testInputStreamOpenEnded — shared test asserting the default getInputStream() returns the full object; runs against every store subclass.
  • HttpStoreTest.testOpenEndedRangeHeader / testBoundedRangeHeader — MockWebServer tests pinning the exact Range header (bytes=0- and bytes=1-3).
  • ReadOnlyZipStoreTest.testUnreadableArchiveThrows — asserts StoreException for a missing archive.

Verified against the reproducer from #100 (https://static.webknossos.org/misc/6001240.ozx):

getSize: 39374104
get(k,0): 39374104
get(k,-22): 22
getInputStream(k,0,-1): ok, first bytes read=4
zip children:  zarr.json 0 1 2
MISSING ARCHIVE throws: Failed to read from store '.../does-not-exist.ozx' at key '': Could not open the ZIP archive from the underlying store

🤖 Generated with Claude Code

normanrz and others added 2 commits September 9, 2026 16:08
…e zip

HttpStore.getInputStream formatted "bytes=%d-%d" unconditionally, so the
default Store.getInputStream(keys) — which delegates to (keys, 0, -1) —
sent "bytes=0--1" and returned null. Emit "bytes=<start>-" when end is
negative, mirroring get(keys, start). S3Store had the identical bug.

ReadOnlyZipStore.ensureCache treated a null stream from the underlying
store as an empty archive, turning a read failure into valid-but-empty
data. Throw StoreException.readFailed instead; a genuinely empty archive
still parses to an empty index through the normal path.

Fixes #100
Fixes #101

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@normanrz normanrz self-assigned this Sep 9, 2026
@normanrz
normanrz merged commit 7e6adac into main Sep 9, 2026
3 checks passed
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.

HttpStore.getInputStream doesn't support open-ended ranges, unlike HttpStore.get

1 participant