This is an automated email from the ASF dual-hosted git repository.
zeroshade pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/arrow-go.git
The following commit(s) were added to refs/heads/main by this push:
new 558d1d8b fix(arrow/ipc): return errors for malformed file record
batches (#1062)
558d1d8b is described below
commit 558d1d8b0609be121e3e6ebe3ca3794ac9267326
Author: Minh Vu <[email protected]>
AuthorDate: Wed Aug 5 19:56:19 2026 +0200
fix(arrow/ipc): return errors for malformed file record batches (#1062)
### Rationale for this change
IPC stream reads convert record-loading panics from malformed messages
into errors. `FileReader.RecordBatchAt` used the same loader without
that boundary, so malformed record metadata could panic through an API
that returns an error.
### What changes are included in this PR?
Add an error-returning boundary around file record construction using
the same recovered-error formatting as the stream reader. The explicit
record-index bounds panic remains unchanged.
### Are these changes tested?
Yes. A regression test supplies malformed metadata and verifies an error
with no record.
`go test ./arrow/ipc -run
TestLoadRecordBatchReturnsMalformedMetadataErrors`
### Are there any user-facing changes?
Corrupt IPC file batches now return an error instead of panicking.
---
arrow/ipc/file_reader.go | 13 +++++++++++-
arrow/ipc/file_reader_internal_test.go | 38 ++++++++++++++++++++++++++++++++++
2 files changed, 50 insertions(+), 1 deletion(-)
diff --git a/arrow/ipc/file_reader.go b/arrow/ipc/file_reader.go
index aa4e3770..3128a150 100644
--- a/arrow/ipc/file_reader.go
+++ b/arrow/ipc/file_reader.go
@@ -32,6 +32,7 @@ import (
"github.com/apache/arrow-go/v18/arrow/internal/dictutils"
"github.com/apache/arrow-go/v18/arrow/internal/flatbuf"
"github.com/apache/arrow-go/v18/arrow/memory"
+ "github.com/apache/arrow-go/v18/internal/utils"
)
type readerImpl interface {
@@ -467,7 +468,17 @@ func (f *FileReader) RecordBatchAt(i int)
(arrow.RecordBatch, error) {
return nil, fmt.Errorf("arrow/ipc: message %d is not a
RecordBatch", i)
}
- return newRecordBatch(f.schema, &f.memo, msg.meta, msg.body,
f.swapEndianness, f.mem), nil
+ return loadRecordBatch(f.schema, &f.memo, msg.meta, msg.body,
f.swapEndianness, f.mem)
+}
+
+func loadRecordBatch(schema *arrow.Schema, memo *dictutils.Memo, meta, body
*memory.Buffer, swapEndianness bool, mem memory.Allocator) (rec
arrow.RecordBatch, err error) {
+ defer func() {
+ if pErr := recover(); pErr != nil {
+ rec = nil
+ err = utils.FormatRecoveredError("arrow/ipc: error
reading record batch", pErr)
+ }
+ }()
+ return newRecordBatch(schema, memo, meta, body, swapEndianness, mem),
nil
}
// RecordAt returns the i-th record from the file. Ownership is transferred to
the
diff --git a/arrow/ipc/file_reader_internal_test.go
b/arrow/ipc/file_reader_internal_test.go
new file mode 100644
index 00000000..93ab1c39
--- /dev/null
+++ b/arrow/ipc/file_reader_internal_test.go
@@ -0,0 +1,38 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package ipc
+
+import (
+ "testing"
+
+ "github.com/apache/arrow-go/v18/arrow"
+ "github.com/apache/arrow-go/v18/arrow/internal/dictutils"
+ "github.com/apache/arrow-go/v18/arrow/memory"
+ "github.com/stretchr/testify/require"
+)
+
+func TestLoadRecordBatchReturnsMalformedMetadataErrors(t *testing.T) {
+ meta := memory.NewBufferBytes([]byte{0})
+ defer meta.Release()
+ body := memory.NewBufferBytes(nil)
+ defer body.Release()
+
+ rec, err := loadRecordBatch(arrow.NewSchema(nil, nil),
&dictutils.Memo{}, meta, body, false, memory.DefaultAllocator)
+ require.Error(t, err)
+ require.Nil(t, rec)
+}