{"record":{"id":"760a980750ccb61a","repo":"googleapis/mcp-toolbox","slug":"failed-to-open-object-q-in-bucket-q-w","errorCode":null,"errorMessage":"failed to open object %q in bucket %q: %w","messagePattern":"failed to open object %q in bucket %q: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/sources/cloudstorage/cloudstorage.go","lineNumber":254,"sourceCode":"// content, its content type, and the number of bytes read. offset and length\n// follow storage.ObjectHandle.NewRangeReader semantics: length == -1 means\n// \"read to end of object\"; a negative offset means \"suffix from end\" (in\n// which case length must be -1). Reads larger than defaultMaxReadBytes are\n// rejected with cloudstoragecommon.ErrReadSizeLimitExceeded so the caller can\n// narrow the range. Objects whose bytes are not valid UTF-8 are rejected\n// with cloudstoragecommon.ErrBinaryContent.\n//\n// TODO: MCP tool results only carry text today, so we gate this tool on\n// utf8.Valid. When the toolbox supports non-text MCP content (embedded\n// resources, images, blobs), expand this to detect content type and return\n// binary payloads natively.\nfunc (s *Source) ReadObject(ctx context.Context, bucket, object string, offset, length int64) (map[string]any, error) {\n\tif err := s.validateBucket(bucket); err != nil {\n\t\treturn nil, err\n\t}\n\treader, err := s.client.Bucket(bucket).Object(object).NewRangeReader(ctx, offset, length)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to open object %q in bucket %q: %w\", object, bucket, err)\n\t}\n\tdefer reader.Close()\n\n\tif remain := reader.Remain(); remain > defaultMaxReadBytes {\n\t\treturn nil, fmt.Errorf(\"object %q: %d bytes exceeds %d byte limit: %w\",\n\t\t\tobject, remain, defaultMaxReadBytes,\n\t\t\tcloudstoragecommon.ErrReadSizeLimitExceeded)\n\t}\n\n\tdata, err := io.ReadAll(reader)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to read object %q in bucket %q: %w\", object, bucket, err)\n\t}\n\n\tif !utf8.Valid(data) {\n\t\treturn nil, fmt.Errorf(\"object %q in bucket %q: %w\", object, bucket,\n\t\t\tcloudstoragecommon.ErrBinaryContent)\n\t}","sourceCodeStart":236,"sourceCodeEnd":272,"githubUrl":"https://github.com/googleapis/mcp-toolbox/blob/8cc6e09de2ad7b8bffc77751799585a1401a48eb/internal/sources/cloudstorage/cloudstorage.go#L236-L272","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nres, err := source.ReadObject(ctx, \"bkt\", \"reports/2026-q3.csv\", 0, -1) // object may not exist\n// after\nobjs, _ := source.ListObjects(ctx, \"bkt\", \"reports/\", 100, \"\")\nif !contains(objs, \"reports/2026-q3.csv\") { return fmt.Errorf(\"object missing\") }\nres, err := source.ReadObject(ctx, \"bkt\", \"reports/2026-q3.csv\", 0, -1)","handlingStrategy":"try-catch","validationCode":"objs, err := source.ListObjects(ctx, bucket, object, 1, \"\")\nif err != nil || len(objs.Objects) == 0 {\n    return fmt.Errorf(\"object %s/%s does not exist\", bucket, object)\n}","typeGuard":null,"tryCatchPattern":"var e *apierror.APIError\nif errors.As(err, &e) && e.HTTPCode() == 404 {\n    // object missing: inform user or check name\n} else if e != nil && e.HTTPCode() == 403 {\n    // fix IAM\n} else {\n    // retry with backoff\n}","preventionTips":["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."],"tags":["gcs","network","iam","read-object","not-found","cloudstorage"],"backgroundTag":"gcs-object-not-found","analyzedSha":"8cc6e09de2ad7b8bffc77751799585a1401a48eb","analyzedAt":"2026-09-05T01:10:36.887Z","contentChangedAt":"2026-09-05T01:10:36.887Z","schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}