You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@iceberg.apache.org by at...@apache.org on 2026/08/04 10:32:28 UTC

(iceberg-go) branch main updated: fix(io): wrap blob Open failures as path errors (#1577)

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 <vu...@gmail.com>
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 <vu...@gmail.com>
---
 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