diff --git a/sdk/api/handlers/handlers.go b/sdk/api/handlers/handlers.go index 8893e5d6..14ba83ce 100644 --- a/sdk/api/handlers/handlers.go +++ b/sdk/api/handlers/handlers.go @@ -5,6 +5,7 @@ package handlers import ( "bytes" + "encoding/json" "fmt" "net/http" "strings" @@ -437,12 +438,50 @@ func (h *BaseAPIHandler) WriteErrorResponse(c *gin.Context, msg *interfaces.Erro } } } - c.Status(status) + + errText := http.StatusText(status) if msg != nil && msg.Error != nil { - _, _ = c.Writer.Write([]byte(msg.Error.Error())) - } else { - _, _ = c.Writer.Write([]byte(http.StatusText(status))) + if v := strings.TrimSpace(msg.Error.Error()); v != "" { + errText = v + } } + + // Prefer preserving upstream JSON error bodies when possible. + buildJSONBody := func() []byte { + trimmed := strings.TrimSpace(errText) + if trimmed != "" && json.Valid([]byte(trimmed)) { + return []byte(trimmed) + } + errType := "invalid_request_error" + switch status { + case http.StatusUnauthorized: + errType = "authentication_error" + case http.StatusForbidden: + errType = "permission_error" + case http.StatusTooManyRequests: + errType = "rate_limit_error" + default: + if status >= http.StatusInternalServerError { + errType = "server_error" + } + } + payload, err := json.Marshal(ErrorResponse{ + Error: ErrorDetail{ + Message: errText, + Type: errType, + }, + }) + if err != nil { + return []byte(fmt.Sprintf(`{"error":{"message":%q,"type":"server_error"}}`, errText)) + } + return payload + } + + if !c.Writer.Written() { + c.Writer.Header().Set("Content-Type", "application/json") + } + c.Status(status) + _, _ = c.Writer.Write(buildJSONBody()) } func (h *BaseAPIHandler) LoggingAPIResponseError(ctx context.Context, err *interfaces.ErrorMessage) {