Hacktoberfest 2026: the issues maintainers tagged for October, open and beginner-friendly. Browse Hacktoberfest issues

bug: read reopens with a zero-size range after the last byte

Open Beginner friendly
#8,342 0 comments 1 reaction 0 assignees View on GitHub

Nobody has claimed this yet.

Assessment

Difficulty
2/5
Estimated time
1-3 hours
Newbie friendliness
86/100
Issue type
Bug
Clarity
Clearly specified
Activity status
Active
Tech stack
rust

Research direction

Start in core/layers/retry/src/lib.rs at RetryReadStream::read and inspect the existing mock and test_retry_read. Run the provided bounded-range reproduction, then verify that the read completes without reopening after the last byte and that the relevant retry tests pass.

Written by the indexing model from the issue text.

Description

bug core
OpenDAL version or commit

main (53610f89), and 0.58.0 onward

Affected service or component

core: layers/retry, RetryReadStream::read. Service-independent.

Describe the bug

RetryReadStream::read advances the range by what it has read before reopening (core/layers/retry/src/lib.rs:657). If the stream breaks after the last byte of a bounded range, the range is already exhausted, and it reopens with size zero.

Nothing is left to fetch, and a zero-size range has no valid Range header: Display returns Err for it, so any layer that formats the range panics.

Steps to Reproduce

Against the mock already in core/layers/retry/src/lib.rs, which returns the whole body and then a temporary error:

#[tokio::test]
async fn test_reopen_at_eof() -> Result<()> {
    let op = Operator::new(MockBuilder::default())?
        .layer(LoggingLayer::default())
        .layer(RetryLayer::default());

    let r = op.reader("retryable_error").await?;
    let mut buf = Vec::new();
    r.read_into(&mut buf, 0..13).await?; // panics
    Ok(())
}

test_retry_read does not panic because it reads unbounded: that range advances to 13-, which formats fine. Only a size of zero fails.

I found this by reading the code, and the reproduction above is that in-repo mock. I have not seen it against a live backend, which would need a connection failure after the last byte of a bounded range.

Actual Behavior
panicked at library/alloc/src/string.rs:2943:14:
a Display implementation returned an error unexpectedly: Error

Without a formatting layer there is no panic, but the request is still sent: is_full() is false for a zero-size range, so S3 for one sets the header (core/services/s3/src/core.rs:585) with the empty string to_header returns.

Expected Behavior

The read ends once the range is exhausted, instead of reopening.

Additional Context

#8084 made to_header return an empty string for such a range instead of panicking, and noted there that an empty header is not right either. This is the other end of it: the range should not be created at all.

Reproduction confirmation
  • I reproduced this issue against the affected component. For a service-specific report, I used the actual service rather than only a mock, emulator, or source-code analysis.
Are you willing to submit a PR to fix this bug?
  • Yes, I would like to submit a PR.
Dominant language
Rust
Stars
5.4k
Forks
825
Avg merge
16h 49m
Merged PRs (30d)
112

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from apache/opendal

All issues in apache/opendal

Similar issues

More Rust issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.