This is an automated email from the ASF dual-hosted git repository.

laskoviymishka pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/iceberg-go.git


The following commit(s) were added to refs/heads/main by this push:
     new 5079bc105 fix(io): wrap blob Open failures as path errors (#1577)
5079bc105 is described below

commit 5079bc105297667ad0e4fcdfe3d1d3748fda3df7
Author: Minh Vu <[email protected]>
AuthorDate: Tue Aug 4 12:32:22 2026 +0200

    fix(io): wrap blob Open failures as path errors (#1577)
    
    ## What changed
    
    Wrap blob reader failures from `BlobFileIO.Open` with the existing
    filesystem error adapter and retain the caller-provided path for
    preprocessing, validation, and backend failures.
    
    ## Why
    
    Backend errors were returned directly, contrary to the `IO.Open`
    contract. Missing objects did not consistently expose `fs.ErrNotExist`
    through a `*fs.PathError`, and preprocessing could replace the public
    path with an empty or internal value.
    
    ## Testing
    
    - `go test ./io/gocloud -run
    'TestBlobFileIOOpenMissingReturnsPathError|TestDefaultKeyExtractor'`
    - `go vet ./io/gocloud`
    
    ---------
    
    Signed-off-by: Minh Vu <[email protected]>
---
 io/gocloud/blob.go      |  7 ++++---
 io/gocloud/blob_test.go | 34 ++++++++++++++++++++++++++++++++++
 2 files changed, 38 insertions(+), 3 deletions(-)

diff --git a/io/gocloud/blob.go b/io/gocloud/blob.go
index fc38185fb..46a3083b6 100644
--- a/io/gocloud/blob.go
+++ b/io/gocloud/blob.go
@@ -249,20 +249,21 @@ func directoryName(key string) string {
 }
 
 func (bfs *BlobFileIO) Open(path string) (icebergio.File, error) {
+       originalPath := path
        var err error
        path, err = bfs.preprocess(path)
        if err != nil {
-               return nil, &fs.PathError{Op: "open", Path: path, Err: err}
+               return nil, &fs.PathError{Op: "open", Path: originalPath, Err: 
err}
        }
        if !fs.ValidPath(path) {
-               return nil, &fs.PathError{Op: "open", Path: path, Err: 
fs.ErrInvalid}
+               return nil, &fs.PathError{Op: "open", Path: originalPath, Err: 
fs.ErrInvalid}
        }
 
        key, name := path, pathpkg.Base(path)
 
        r, err := bfs.NewReader(bfs.ctx, key, nil)
        if err != nil {
-               return nil, err
+               return nil, blobErrToFsErr("open", originalPath, err)
        }
 
        return &blobOpenFile{Reader: r, name: name, key: key, b: bfs, ctx: 
bfs.ctx}, nil
diff --git a/io/gocloud/blob_test.go b/io/gocloud/blob_test.go
index ef9f16e98..ba6bf399d 100644
--- a/io/gocloud/blob_test.go
+++ b/io/gocloud/blob_test.go
@@ -162,6 +162,40 @@ func TestDefaultKeyExtractor(t *testing.T) {
        }
 }
 
+func TestBlobFileIOOpenMissingReturnsPathError(t *testing.T) {
+       t.Parallel()
+
+       bucket := memblob.OpenBucket(nil)
+       t.Cleanup(func() { require.NoError(t, bucket.Close()) })
+       fileIO := testBlobFileIO(context.Background(), "my-bucket", bucket)
+       name := "s3://my-bucket/missing.parquet"
+
+       _, err := fileIO.Open(name)
+       require.ErrorIs(t, err, fs.ErrNotExist)
+
+       var pathErr *fs.PathError
+       require.ErrorAs(t, err, &pathErr)
+       assert.Equal(t, "open", pathErr.Op)
+       assert.Equal(t, name, pathErr.Path)
+}
+
+func TestBlobFileIOOpenPreprocessErrorRetainsOriginalPath(t *testing.T) {
+       t.Parallel()
+
+       bucket := memblob.OpenBucket(nil)
+       t.Cleanup(func() { require.NoError(t, bucket.Close()) })
+       fileIO := testBlobFileIO(context.Background(), "my-bucket", bucket)
+       name := "s3://other-bucket/file.parquet"
+
+       _, err := fileIO.Open(name)
+       require.Error(t, err)
+
+       var pathErr *fs.PathError
+       require.ErrorAs(t, err, &pathErr)
+       assert.Equal(t, "open", pathErr.Op)
+       assert.Equal(t, name, pathErr.Path)
+}
+
 func testBlobFileIO(ctx context.Context, bucketName string, bucket 
*blob.Bucket, allowedSchemes ...string) *BlobFileIO {
        if len(allowedSchemes) == 0 {
                allowedSchemes = s3Schemes

Reply via email to