// Copyright 2026 Google LLC // Licensed 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 util import ( "errors" "fmt" "net/http" "strings" "google.golang.org/api/googleapi" ) type ErrorCategory string const ( CategoryAgent ErrorCategory = "AGENT_ERROR" CategoryServer ErrorCategory = "SERVER_ERROR" ) // ToolboxError is the interface all custom errors must satisfy type ToolboxError interface { error Category() ErrorCategory } // Agent Errors return 200 to the sender type AgentError struct { Msg string Cause error } func (e *AgentError) Error() string { return e.Msg } func (e *AgentError) Category() ErrorCategory { return CategoryAgent } func (e *AgentError) Unwrap() error { return e.Cause } func NewAgentError(msg string, cause error) *AgentError { return &AgentError{Msg: msg, Cause: cause} } // ClientServerError returns 4XX/5XX error code type ClientServerError struct { Msg string Code int Cause error } func (e *ClientServerError) Error() string { return fmt.Sprintf("%s: %v", e.Msg, e.Cause) } func (e *ClientServerError) Category() ErrorCategory { return CategoryServer } func (e *ClientServerError) Unwrap() error { return e.Cause } func NewClientServerError(msg string, code int, cause error) *ClientServerError { return &ClientServerError{Msg: msg, Code: code, Cause: cause} } // ProecessGcpError catches auth related errors and return 401/403 error codes // Returns AgentError for all other errors func ProecessGcpError(err error) ToolboxError { var gErr *googleapi.Error if errors.As(err, &gErr) { if gErr.Code == 401 { return NewClientServerError( "failed to access GCP resource", http.StatusUnauthorized, err, ) } if gErr.Code == 403 { return NewClientServerError( "failed to access GCP resource", http.StatusForbidden, err, ) } } return NewAgentError("error processing GCP request", err) } // ProcessGeneralError handles generic errors by inspecting the error string // for common status code patterns. func ProcessGeneralError(err error) ToolboxError { if err == nil { return nil } errStr := err.Error() // Check for Unauthorized if strings.Contains(errStr, "Error 401") || strings.Contains(errStr, "status 401") { return NewClientServerError( "failed to access resource", http.StatusUnauthorized, err, ) } // Check for Forbidden if strings.Contains(errStr, "Error 403") || strings.Contains(errStr, "status 403") { return NewClientServerError( "failed to access resource", http.StatusForbidden, err, ) } // Default to AgentError for logical failures (task execution failed) return NewAgentError("error processing request", err) }