// Copyright 2026 MarketAlly. All rights reserved. // SPDX-License-Identifier: MIT package errors import ( "fmt" "code.gitea.io/gitea/modules/setting" ) // APIErrorResponse is the top-level error response wrapper type APIErrorResponse struct { Error *APIError `json:"error"` } // APIError represents a structured API error following RFC 7807 Problem Details // with additional fields for AI-friendly error handling type APIError struct { // Machine-readable error code (e.g., "REPO_NOT_FOUND") Code string `json:"code"` // Human-readable error message Message string `json:"message"` // HTTP status code Status int `json:"status"` // Additional context about the error Details map[string]any `json:"details,omitempty"` // URL to documentation about this error DocumentationURL string `json:"documentation_url,omitempty"` // Unique request ID for tracing RequestID string `json:"request_id,omitempty"` // Suggested actions or alternatives Suggestions []string `json:"suggestions,omitempty"` // RFC 7807 Problem Details fields Type string `json:"type,omitempty"` // URI reference identifying the problem type Title string `json:"title,omitempty"` // Short summary of the problem type Instance string `json:"instance,omitempty"` // URI reference for this specific occurrence } // NewAPIError creates a new structured API error func NewAPIError(code ErrorCode, requestID string) *APIError { docURL := fmt.Sprintf("%s/api/errors#%s", setting.AppURL, code) return &APIError{ Code: code.String(), Message: code.Message(), Status: code.HTTPStatus(), DocumentationURL: docURL, RequestID: requestID, Type: "about:blank", Title: code.Message(), Instance: requestID, } } // WithDetails adds details to the error func (e *APIError) WithDetails(details map[string]any) *APIError { e.Details = details return e } // WithDetail adds a single detail to the error func (e *APIError) WithDetail(key string, value any) *APIError { if e.Details == nil { e.Details = make(map[string]any) } e.Details[key] = value return e } // WithMessage overrides the default message func (e *APIError) WithMessage(message string) *APIError { e.Message = message e.Title = message return e } // WithSuggestions adds suggested actions func (e *APIError) WithSuggestions(suggestions ...string) *APIError { e.Suggestions = append(e.Suggestions, suggestions...) return e } // Response wraps the error in an APIErrorResponse func (e *APIError) Response() *APIErrorResponse { return &APIErrorResponse{Error: e} } // ValidationError represents a field-level validation error type ValidationError struct { Field string `json:"field"` Message string `json:"message"` Code string `json:"code,omitempty"` } // APIValidationError represents a validation error with field-level details type APIValidationError struct { *APIError Errors []ValidationError `json:"errors,omitempty"` } // NewValidationError creates a new validation error func NewValidationError(requestID string, errors ...ValidationError) *APIValidationError { baseErr := NewAPIError(ValInvalidInput, requestID) return &APIValidationError{ APIError: baseErr, Errors: errors, } } // AddFieldError adds a field validation error func (e *APIValidationError) AddFieldError(field, message string, code ...string) *APIValidationError { ve := ValidationError{ Field: field, Message: message, } if len(code) > 0 { ve.Code = code[0] } e.Errors = append(e.Errors, ve) return e }