Gate stable_only=True before bbox metadata read on remote/VRT sources#2882
Merged
Conversation
…#2869) open_geotiff(..., bbox=...) resolved the bbox to a pixel window before the stable_only=True rejection ran for remote and VRT sources. The bbox resolver reads source geo metadata first (a header range GET for an HTTP/fsspec source, a VRT XML parse for a .vrt source), so a stable-only request could trigger remote I/O or a VRT parse before it was refused. That breaks the ordering contract on _validate_stable_only_remote. Move both stable-only gates ahead of bbox resolution: _validate_stable_only_vrt for .vrt sources, _validate_stable_only_remote otherwise. The old post-VRT remote gate is removed since it now runs earlier. read_vrt keeps its own gate for direct callers. Adds tests covering remote+bbox+stable_only and vrt+bbox+stable_only, each pointed at a source whose bbox read would raise a different error if it ran first, so the typed stable-only error proves the ordering.
brendancol
commented
Jun 2, 2026
Contributor
Author
brendancol
left a comment
There was a problem hiding this comment.
PR Review: Gate stable_only=True before bbox metadata read on remote/VRT sources
Blockers (must fix before merge)
None.
Suggestions (should fix, not blocking)
None.
Nits (optional improvements)
xrspatial/geotiff/__init__.py:859-870: moving the stable-only gate above the bbox block also moves it above thewindow=/bbox=mutual-exclusionValueError(now at :880-884). Soopen_geotiff(remote, window=X, bbox=Y, stable_only=True)now raises the stable-only error instead of the mutual-exclusion error. That precedence is defensible (reject the unsupported tier before validating kwargs that won't be honored anyway) and probably more correct, but it is a small change in error ordering that nothing tests. Optional: note it in the comment, or leave as-is.
What looks good
- The fix matches the contract documented on
_validate_stable_only_remote("before any range GET or decode work") and extends the same ordering to the VRT XML parse. - Tests are well-targeted. Each points at a source whose bbox read would raise a distinct error (fetch failure / xml
ParseError) if it ran first, so the typed stable-only error proves the ordering rather than just the eventual rejection. The negative control (malformed VRT without stable_only still hitsParseError) confirms the fixture exercises the path being short-circuited. - The redundant post-VRT-branch
_validate_stable_only_remotecall is removed cleanly.read_vrtkeeps its own gate, so direct callers still fail closed. - Positive controls with
allow_experimental_codecs=Trueconfirm the bbox still resolves and reads when the tier is unlocked.
Checklist
- Validation ordering matches the documented contract
- Remote (HTTP + fsspec) and VRT bbox paths covered by tests
- Eager and dask dispatch paths both exercised
- No new public API; no README or notebook needed
- No numerical/kernel change; backend parity not affected
- Diff is surgical, no unrelated edits
…nce (#2869) The stable-only gate now runs ahead of the window=/bbox= mutual-exclusion check. Note that precedence in the comment and add a test pinning it: a remote source under stable_only=True with both window= and bbox= reports the tier rejection, not the kwarg conflict.
brendancol
commented
Jun 2, 2026
Contributor
Author
brendancol
left a comment
There was a problem hiding this comment.
Follow-up review
The one nit from the first pass is addressed in a1c75a2:
- The comment at
xrspatial/geotiff/__init__.py:854-861now spells out that the stable-only gate runs ahead of thewindow=/bbox=mutual-exclusion check, so the tier rejection wins. test_stable_only_wins_over_window_bbox_conflictpins that precedence: a remote source understable_only=Truewith bothwindow=andbbox=reportsRemoteStableSourcesOnlyError, not the kwarg conflict.
No new findings. The full test_stable_only_bbox_ordering_2869.py suite (8 tests) passes, along with the existing stable-only, bbox, and geotiff release-gate suites. The branch is even with origin/main with no conflicts. Nothing blocking.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #2869
open_geotiff(..., bbox=...)resolved the bounding box to a pixel window before thestable_only=Truerejection ran for remote and VRT sources. The bbox resolver reads source geo metadata first: the TIFF path reads a header (a range GET for an HTTP/fsspec source) and the VRT path parses the VRT XML. Both stable-only gates only fired after that, so astable_only=Truerequest could trigger remote I/O or a VRT parse before it got refused. The docstring on_validate_stable_only_remoterequires the rejection to land before any range GET or decode work.Changes
open_geotiff:_validate_stable_only_vrtfor.vrtsources,_validate_stable_only_remoteotherwise. Each helper is a no-op for the wrong source type._validate_stable_only_remotecall, now redundant since the gate runs earlier.read_vrtkeeps its own gate for direct callers, so the VRT path still fails closed on both entry points.Backend coverage
Dispatcher-level validation, so it applies uniformly across numpy / dask. The fsspec + bbox case is exercised through both the eager and dask dispatch paths. No GPU or numerical kernel change.
Test plan
test_stable_only_bbox_ordering_2869.py: remote (HTTP + fsspec) and VRT bbox + stable_only combinations, each pointed at a source whose bbox read would raise a different error (fetch failure / XMLParseError) if it ran first, so the typed stable-only error proves the gate runs ahead of the read.allow_experimental_codecs=Truestill resolves the bbox and reads for both fsspec and VRT sources.test_stable_only_remote_2821.py, bbox suites, and the geotiff release-gate tests pass unchanged.