// Copyright 2026 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT package errors import "net/http" // ErrorCode represents a machine-readable error code type ErrorCode string // Authentication errors (AUTH_) const ( AuthTokenMissing ErrorCode = "AUTH_TOKEN_MISSING" AuthTokenInvalid ErrorCode = "AUTH_TOKEN_INVALID" AuthTokenExpired ErrorCode = "AUTH_TOKEN_EXPIRED" AuthScopeInsufficient ErrorCode = "AUTH_SCOPE_INSUFFICIENT" Auth2FARequired ErrorCode = "AUTH_2FA_REQUIRED" AuthInvalidCredentials ErrorCode = "AUTH_INVALID_CREDENTIALS" ) // Permission errors (PERM_) const ( PermRepoReadDenied ErrorCode = "PERM_REPO_READ_DENIED" PermRepoWriteDenied ErrorCode = "PERM_REPO_WRITE_DENIED" PermRepoAdminRequired ErrorCode = "PERM_REPO_ADMIN_REQUIRED" PermOrgMemberRequired ErrorCode = "PERM_ORG_MEMBER_REQUIRED" PermOrgAdminRequired ErrorCode = "PERM_ORG_ADMIN_REQUIRED" PermActionDenied ErrorCode = "PERM_ACTION_DENIED" ) // Repository errors (REPO_) const ( RepoNotFound ErrorCode = "REPO_NOT_FOUND" RepoArchived ErrorCode = "REPO_ARCHIVED" RepoDisabled ErrorCode = "REPO_DISABLED" RepoTransferPending ErrorCode = "REPO_TRANSFER_PENDING" RepoEmpty ErrorCode = "REPO_EMPTY" RepoAlreadyExists ErrorCode = "REPO_ALREADY_EXISTS" ) // File errors (FILE_) const ( FileNotFound ErrorCode = "FILE_NOT_FOUND" FileTooLarge ErrorCode = "FILE_TOO_LARGE" FileConflict ErrorCode = "FILE_CONFLICT" FileBinary ErrorCode = "FILE_BINARY" FileTypeError ErrorCode = "FILE_TYPE_NOT_ALLOWED" ) // Git errors (GIT_) const ( GitRefNotFound ErrorCode = "GIT_REF_NOT_FOUND" GitMergeConflict ErrorCode = "GIT_MERGE_CONFLICT" GitBranchNotFound ErrorCode = "GIT_BRANCH_NOT_FOUND" GitTagNotFound ErrorCode = "GIT_TAG_NOT_FOUND" GitCommitNotFound ErrorCode = "GIT_COMMIT_NOT_FOUND" GitPushRejected ErrorCode = "GIT_PUSH_REJECTED" ) // Rate limiting errors (RATE_) const ( RateLimitExceeded ErrorCode = "RATE_LIMIT_EXCEEDED" RateQuotaExhausted ErrorCode = "RATE_QUOTA_EXHAUSTED" ) // Validation errors (VAL_) const ( ValInvalidInput ErrorCode = "VAL_INVALID_INPUT" ValMissingField ErrorCode = "VAL_MISSING_FIELD" ValInvalidName ErrorCode = "VAL_INVALID_NAME" ValNameTooLong ErrorCode = "VAL_NAME_TOO_LONG" ValInvalidEmail ErrorCode = "VAL_INVALID_EMAIL" ValDuplicateName ErrorCode = "VAL_DUPLICATE_NAME" ValInvalidFormat ErrorCode = "VAL_INVALID_FORMAT" ValidationFailed ErrorCode = "VALIDATION_FAILED" ) // General errors const ( InternalError ErrorCode = "INTERNAL_ERROR" PermAccessDenied ErrorCode = "ACCESS_DENIED" RefNotFound ErrorCode = "REF_NOT_FOUND" ) // Upload errors (UPLOAD_) const ( UploadSessionNotFound ErrorCode = "UPLOAD_SESSION_NOT_FOUND" UploadSessionExpired ErrorCode = "UPLOAD_SESSION_EXPIRED" UploadChunkInvalid ErrorCode = "UPLOAD_CHUNK_INVALID" UploadChunkSizeMismatch ErrorCode = "UPLOAD_CHUNK_SIZE_MISMATCH" UploadChecksumMismatch ErrorCode = "UPLOAD_CHECKSUM_MISMATCH" UploadIncomplete ErrorCode = "UPLOAD_INCOMPLETE" UploadFileTooLarge ErrorCode = "UPLOAD_FILE_TOO_LARGE" ) // Resource errors (RESOURCE_) const ( ResourceNotFound ErrorCode = "RESOURCE_NOT_FOUND" ResourceConflict ErrorCode = "RESOURCE_CONFLICT" ResourceGone ErrorCode = "RESOURCE_GONE" ) // Server errors (SERVER_) const ( ServerInternal ErrorCode = "SERVER_INTERNAL_ERROR" ServerUnavailable ErrorCode = "SERVER_UNAVAILABLE" ServerTimeout ErrorCode = "SERVER_TIMEOUT" ) // User errors (USER_) const ( UserNotFound ErrorCode = "USER_NOT_FOUND" UserAlreadyExists ErrorCode = "USER_ALREADY_EXISTS" UserInactive ErrorCode = "USER_INACTIVE" UserProhibitLogin ErrorCode = "USER_PROHIBIT_LOGIN" ) // Organization errors (ORG_) const ( OrgNotFound ErrorCode = "ORG_NOT_FOUND" OrgAlreadyExists ErrorCode = "ORG_ALREADY_EXISTS" ) // Issue errors (ISSUE_) const ( IssueNotFound ErrorCode = "ISSUE_NOT_FOUND" IssueClosed ErrorCode = "ISSUE_CLOSED" IssueLocked ErrorCode = "ISSUE_LOCKED" ) // Pull Request errors (PR_) const ( PRNotFound ErrorCode = "PR_NOT_FOUND" PRAlreadyMerged ErrorCode = "PR_ALREADY_MERGED" PRNotMergeable ErrorCode = "PR_NOT_MERGEABLE" PRWorkInProgress ErrorCode = "PR_WORK_IN_PROGRESS" ) // Release errors (RELEASE_) const ( ReleaseNotFound ErrorCode = "RELEASE_NOT_FOUND" ReleaseTagExists ErrorCode = "RELEASE_TAG_EXISTS" ReleaseIsDraft ErrorCode = "RELEASE_IS_DRAFT" ) // Webhook errors (WEBHOOK_) const ( WebhookNotFound ErrorCode = "WEBHOOK_NOT_FOUND" WebhookDeliveryFail ErrorCode = "WEBHOOK_DELIVERY_FAILED" ) // Wiki errors (WIKI_) const ( WikiPageNotFound ErrorCode = "WIKI_PAGE_NOT_FOUND" WikiPageAlreadyExists ErrorCode = "WIKI_PAGE_ALREADY_EXISTS" WikiReservedName ErrorCode = "WIKI_RESERVED_NAME" WikiDisabled ErrorCode = "WIKI_DISABLED" ) // errorInfo contains metadata about an error code type errorInfo struct { Message string HTTPStatus int } // errorCatalog maps error codes to their metadata var errorCatalog = map[ErrorCode]errorInfo{ // Auth errors AuthTokenMissing: {"No authentication token provided", http.StatusUnauthorized}, AuthTokenInvalid: {"Token is malformed or invalid", http.StatusUnauthorized}, AuthTokenExpired: {"Token has expired", http.StatusUnauthorized}, AuthScopeInsufficient: {"Token lacks required scope", http.StatusForbidden}, Auth2FARequired: {"Two-factor authentication required", http.StatusUnauthorized}, AuthInvalidCredentials: {"Invalid username or password", http.StatusUnauthorized}, // Permission errors PermRepoReadDenied: {"Cannot read repository", http.StatusForbidden}, PermRepoWriteDenied: {"Cannot write to repository", http.StatusForbidden}, PermRepoAdminRequired: {"Repository admin access required", http.StatusForbidden}, PermOrgMemberRequired: {"Must be organization member", http.StatusForbidden}, PermOrgAdminRequired: {"Organization admin access required", http.StatusForbidden}, PermActionDenied: {"Permission denied for this action", http.StatusForbidden}, // Repository errors RepoNotFound: {"Repository does not exist", http.StatusNotFound}, RepoArchived: {"Repository is archived", http.StatusForbidden}, RepoDisabled: {"Repository is disabled", http.StatusForbidden}, RepoTransferPending: {"Repository has pending transfer", http.StatusConflict}, RepoEmpty: {"Repository is empty", http.StatusUnprocessableEntity}, RepoAlreadyExists: {"Repository already exists", http.StatusConflict}, // File errors FileNotFound: {"File does not exist", http.StatusNotFound}, FileTooLarge: {"File exceeds size limit", http.StatusRequestEntityTooLarge}, FileConflict: {"File was modified (SHA mismatch)", http.StatusConflict}, FileBinary: {"Cannot perform text operation on binary file", http.StatusBadRequest}, FileTypeError: {"File type not allowed", http.StatusBadRequest}, // Git errors GitRefNotFound: {"Git reference not found", http.StatusNotFound}, GitMergeConflict: {"Merge conflict detected", http.StatusConflict}, GitBranchNotFound: {"Branch not found", http.StatusNotFound}, GitTagNotFound: {"Tag not found", http.StatusNotFound}, GitCommitNotFound: {"Commit not found", http.StatusNotFound}, GitPushRejected: {"Push rejected", http.StatusForbidden}, // Rate limiting errors RateLimitExceeded: {"API rate limit exceeded", http.StatusTooManyRequests}, RateQuotaExhausted: {"Rate quota exhausted", http.StatusTooManyRequests}, // Validation errors ValInvalidInput: {"Invalid input provided", http.StatusBadRequest}, ValMissingField: {"Required field is missing", http.StatusBadRequest}, ValInvalidName: {"Name contains invalid characters", http.StatusBadRequest}, ValNameTooLong: {"Name exceeds maximum length", http.StatusBadRequest}, ValInvalidEmail: {"Invalid email address", http.StatusBadRequest}, ValDuplicateName: {"Name already exists", http.StatusConflict}, ValInvalidFormat: {"Invalid format", http.StatusBadRequest}, ValidationFailed: {"Validation failed", http.StatusBadRequest}, // General errors InternalError: {"Internal server error", http.StatusInternalServerError}, PermAccessDenied: {"Access denied", http.StatusForbidden}, RefNotFound: {"Reference not found", http.StatusNotFound}, // Upload errors UploadSessionNotFound: {"Upload session does not exist", http.StatusNotFound}, UploadSessionExpired: {"Upload session has expired", http.StatusGone}, UploadChunkInvalid: {"Chunk number out of range", http.StatusBadRequest}, UploadChunkSizeMismatch: {"Chunk size doesn't match expected", http.StatusBadRequest}, UploadChecksumMismatch: {"File checksum verification failed", http.StatusBadRequest}, UploadIncomplete: {"Not all chunks have been uploaded", http.StatusBadRequest}, UploadFileTooLarge: {"File exceeds maximum upload size", http.StatusRequestEntityTooLarge}, // Resource errors ResourceNotFound: {"Resource not found", http.StatusNotFound}, ResourceConflict: {"Resource conflict", http.StatusConflict}, ResourceGone: {"Resource no longer available", http.StatusGone}, // Server errors ServerInternal: {"Internal server error", http.StatusInternalServerError}, ServerUnavailable: {"Service temporarily unavailable", http.StatusServiceUnavailable}, ServerTimeout: {"Request timeout", http.StatusGatewayTimeout}, // User errors UserNotFound: {"User not found", http.StatusNotFound}, UserAlreadyExists: {"User already exists", http.StatusConflict}, UserInactive: {"User account is inactive", http.StatusForbidden}, UserProhibitLogin: {"User is not allowed to login", http.StatusForbidden}, // Organization errors OrgNotFound: {"Organization not found", http.StatusNotFound}, OrgAlreadyExists: {"Organization already exists", http.StatusConflict}, // Issue errors IssueNotFound: {"Issue not found", http.StatusNotFound}, IssueClosed: {"Issue is closed", http.StatusUnprocessableEntity}, IssueLocked: {"Issue is locked", http.StatusForbidden}, // Pull Request errors PRNotFound: {"Pull request not found", http.StatusNotFound}, PRAlreadyMerged: {"Pull request already merged", http.StatusConflict}, PRNotMergeable: {"Pull request is not mergeable", http.StatusConflict}, PRWorkInProgress: {"Pull request is marked as work in progress", http.StatusUnprocessableEntity}, // Release errors ReleaseNotFound: {"Release not found", http.StatusNotFound}, ReleaseTagExists: {"Release tag already exists", http.StatusConflict}, ReleaseIsDraft: {"Release is a draft", http.StatusUnprocessableEntity}, // Webhook errors WebhookNotFound: {"Webhook not found", http.StatusNotFound}, WebhookDeliveryFail: {"Webhook delivery failed", http.StatusBadGateway}, // Wiki errors WikiPageNotFound: {"Wiki page not found", http.StatusNotFound}, WikiPageAlreadyExists: {"Wiki page already exists", http.StatusConflict}, WikiReservedName: {"Wiki page name is reserved", http.StatusBadRequest}, WikiDisabled: {"Wiki is disabled for this repository", http.StatusForbidden}, } // Message returns the human-readable message for an error code func (e ErrorCode) Message() string { if info, ok := errorCatalog[e]; ok { return info.Message } return string(e) } // HTTPStatus returns the HTTP status code for an error code func (e ErrorCode) HTTPStatus() int { if info, ok := errorCatalog[e]; ok { return info.HTTPStatus } return http.StatusInternalServerError } // String returns the error code as a string func (e ErrorCode) String() string { return string(e) } // Error implements the error interface func (e ErrorCode) Error() string { return e.Message() } // IsValid returns true if the error code is registered in the catalog func (e ErrorCode) IsValid() bool { _, ok := errorCatalog[e] return ok }