googleapis/mcp-toolbox · error
failed to open object %q in bucket %q: %w
Error message
failed to open object %q in bucket %q: %w
What it means
NewRangeReader failed when opening the requested object (or byte range) for reading. The source wraps the GCS client error, so the underlying cause (404 not found, 403 denied, network failure) is preserved via %w.
Source
Thrown at internal/sources/cloudstorage/cloudstorage.go:254
// content, its content type, and the number of bytes read. offset and length
// follow storage.ObjectHandle.NewRangeReader semantics: length == -1 means
// "read to end of object"; a negative offset means "suffix from end" (in
// which case length must be -1). Reads larger than defaultMaxReadBytes are
// rejected with cloudstoragecommon.ErrReadSizeLimitExceeded so the caller can
// narrow the range. Objects whose bytes are not valid UTF-8 are rejected
// with cloudstoragecommon.ErrBinaryContent.
//
// TODO: MCP tool results only carry text today, so we gate this tool on
// utf8.Valid. When the toolbox supports non-text MCP content (embedded
// resources, images, blobs), expand this to detect content type and return
// binary payloads natively.
func (s *Source) ReadObject(ctx context.Context, bucket, object string, offset, length int64) (map[string]any, error) {
if err := s.validateBucket(bucket); err != nil {
return nil, err
}
reader, err := s.client.Bucket(bucket).Object(object).NewRangeReader(ctx, offset, length)
if err != nil {
return nil, fmt.Errorf("failed to open object %q in bucket %q: %w", object, bucket, err)
}
defer reader.Close()
if remain := reader.Remain(); remain > defaultMaxReadBytes {
return nil, fmt.Errorf("object %q: %d bytes exceeds %d byte limit: %w",
object, remain, defaultMaxReadBytes,
cloudstoragecommon.ErrReadSizeLimitExceeded)
}
data, err := io.ReadAll(reader)
if err != nil {
return nil, fmt.Errorf("failed to read object %q in bucket %q: %w", object, bucket, err)
}
if !utf8.Valid(data) {
return nil, fmt.Errorf("object %q in bucket %q: %w", object, bucket,
cloudstoragecommon.ErrBinaryContent)
}View on GitHub (pinned to 8cc6e09de2)
Solutions
- Check the wrapped error status: verify the object exists via ListObjects or the GCS console before reading.
- Grant the caller storage.objects.get permission (roles/storage.objectViewer) on the bucket/object.
- Clamp offset/length to the object's size (from object metadata) before calling ReadObject.
- Retry with backoff on transient network/5xx errors; check CMEK key access if the object is encrypted.
Example fix
// before
res, err := source.ReadObject(ctx, "bkt", "reports/2026-q3.csv", 0, -1) // object may not exist
// after
objs, _ := source.ListObjects(ctx, "bkt", "reports/", 100, "")
if !contains(objs, "reports/2026-q3.csv") { return fmt.Errorf("object missing") }
res, err := source.ReadObject(ctx, "bkt", "reports/2026-q3.csv", 0, -1) Defensive patterns
Strategy: try-catch
Validate before calling
objs, err := source.ListObjects(ctx, bucket, object, 1, "")
if err != nil || len(objs.Objects) == 0 {
return fmt.Errorf("object %s/%s does not exist", bucket, object)
} Try / catch
var e *apierror.APIError
if errors.As(err, &e) && e.HTTPCode() == 404 {
// object missing: inform user or check name
} else if e != nil && e.HTTPCode() == 403 {
// fix IAM
} else {
// retry with backoff
} Prevention
- Check object existence via ListObjects before reading.
- Grant storage.objects.get to the caller's service account.
- Clamp offset/length to the object's actual size.
- Retry transient errors with exponential backoff.
When it happens
Trigger: ReadObject called with a bucket/object pair that does not exist, an offset/length outside the object (e.g. offset beyond object size returns ErrObjectNotExist or empty range error), or the caller lacks storage.objects.get IAM permission; also network errors during the initial HTTP request.
Common situations: Typo in object name or missing trailing/extra path segment; object deleted by a lifecycle rule; reading with an offset larger than the object; service account without objectViewer; objects requiring requester-pays or CMEK keys the caller cannot use.
Related errors
- failed to list objects in bucket %q: %w
- failed to read object %q in bucket %q: %w
- object %q: %d bytes exceeds %d byte limit: %w
- failed to list buckets in project %q: %w
- failed to create bucket %q in project %q: %w
AI-assisted analysis of googleapis/mcp-toolbox@8cc6e09de2 (2026-09-05).
Data as JSON: /api/errors/760a980750ccb61a.
Report an issue: GitHub.