From 1d7c4981164c34b4d7bc8edecfd449f57ad11e15 Mon Sep 17 00:00:00 2001 From: mahlevanshika Date: Fri, 23 Jan 2026 19:53:59 +0000 Subject: [PATCH] fix(dataplex): Capture GCP HTTP errors in MCP Toolbox (#2347) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ### Description fix: Surface Dataplex API errors in MCP results This change addresses issue https://github.com/googleapis/genai-toolbox/issues/2203, where Dataplex API errors, such as '403 Forbidden' (Permission Denied), were not being properly surfaced in the MCP tool results. Previously, these critical API errors would manifest as generic "connection interrupted" messages, significantly hindering developer debugging and trust in the Toolbox. The fix enhances the error handling within the 'dataplexsearchentries' and 'dataplexsearchaspecttypes' tools. When an error occurs during the iteration of Dataplex API results, the system now: Utilizes 'google.golang.org/grpc/status.FromError' to attempt to convert the returned error into a gRPC status. This is crucial because Google Cloud client libraries often return errors compatible with gRPC. If the error is a gRPC status, the canonical error code (e.g., 'codes.PermissionDenied') and the associated error message are extracted. This ensures that users receive clear actionable error feedback, allowing for quicker diagnosis and resolution of issues like missing IAM permissions. This aligns with best practices for API error surfacing, improving the usability and reliability of the Dataplex tools within the GenAI Toolbox. Fixes https://github.com/googleapis/genai-toolbox/issues/2203 ## PR Checklist > Thank you for opening a Pull Request! Before submitting your PR, there are a > few things you can do to make sure it goes smoothly: - [ ] Make sure you reviewed [CONTRIBUTING.md](https://github.com/googleapis/genai-toolbox/blob/main/CONTRIBUTING.md) - [ ] Make sure to open an issue as a [bug/issue](https://github.com/googleapis/genai-toolbox/issues/new/choose) before writing your code! That way we can discuss the change, evaluate designs, and agree on the general idea - [ ] Ensure the tests and linter pass - [ ] Code coverage does not decrease (if any source code was changed) - [ ] Appropriate docs were updated (if necessary) - [ ] Make sure to add `!` if this involve a breaking change 🛠️ Fixes # --------- Co-authored-by: Wenxin Du <117315983+duwenxin99@users.noreply.github.com> Co-authored-by: Averi Kitsch --- .github/workflows/link_checker_workflow.yaml | 1 + go.mod | 2 +- internal/sources/dataplex/dataplex.go | 23 ++++++++++++++++++-- 3 files changed, 23 insertions(+), 3 deletions(-) diff --git a/.github/workflows/link_checker_workflow.yaml b/.github/workflows/link_checker_workflow.yaml index 4558f0fa42..ca7d89dfb2 100644 --- a/.github/workflows/link_checker_workflow.yaml +++ b/.github/workflows/link_checker_workflow.yaml @@ -39,6 +39,7 @@ jobs: --no-progress --cache --max-cache-age 1d + --exclude '^neo4j\+.*' --exclude '^bolt://.*' README.md docs/ output: /tmp/foo.txt diff --git a/go.mod b/go.mod index 3782f4b446..2254089cee 100644 --- a/go.mod +++ b/go.mod @@ -63,6 +63,7 @@ require ( google.golang.org/api v0.256.0 google.golang.org/genai v1.37.0 google.golang.org/genproto v0.0.0-20251022142026-3a174f9686a8 + google.golang.org/grpc v1.76.0 google.golang.org/protobuf v1.36.10 modernc.org/sqlite v1.40.0 ) @@ -229,7 +230,6 @@ require ( golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da // indirect google.golang.org/genproto/googleapis/api v0.0.0-20251111163417-95abcf5c77ba // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20251103181224-f26f9409b101 // indirect - google.golang.org/grpc v1.76.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/ini.v1 v1.67.0 // indirect modernc.org/libc v1.66.10 // indirect diff --git a/internal/sources/dataplex/dataplex.go b/internal/sources/dataplex/dataplex.go index 9ddb05edd1..52d64b194d 100644 --- a/internal/sources/dataplex/dataplex.go +++ b/internal/sources/dataplex/dataplex.go @@ -26,7 +26,9 @@ import ( "github.com/googleapis/genai-toolbox/internal/util" "go.opentelemetry.io/otel/trace" "golang.org/x/oauth2/google" + "google.golang.org/api/iterator" "google.golang.org/api/option" + grpcstatus "google.golang.org/grpc/status" ) const SourceKind string = "dataplex" @@ -173,9 +175,18 @@ func (s *Source) SearchAspectTypes(ctx context.Context, query string, pageSize i var results []*dataplexpb.AspectType for { entry, err := it.Next() - if err != nil { + + if err == iterator.Done { break } + if err != nil { + if st, ok := grpcstatus.FromError(err); ok { + errorCode := st.Code() + errorMessage := st.Message() + return nil, fmt.Errorf("failed to search aspect types with error code: %q message: %s", errorCode.String(), errorMessage) + } + return nil, fmt.Errorf("failed to search aspect types: %w", err) + } // Create an instance of exponential backoff with default values for retrying GetAspectType calls // InitialInterval, RandomizationFactor, Multiplier, MaxInterval = 500 ms, 0.5, 1.5, 60 s @@ -214,9 +225,17 @@ func (s *Source) SearchEntries(ctx context.Context, query string, pageSize int, var results []*dataplexpb.SearchEntriesResult for { entry, err := it.Next() - if err != nil { + if err == iterator.Done { break } + if err != nil { + if st, ok := grpcstatus.FromError(err); ok { + errorCode := st.Code() + errorMessage := st.Message() + return nil, fmt.Errorf("failed to search entries with error code: %q message: %s", errorCode.String(), errorMessage) + } + return nil, fmt.Errorf("failed to search entries: %w", err) + } results = append(results, entry) } return results, nil