Files
gitea/modules/errors/codes.go
logikonline b816ee4eec feat: add Phases 3-5 enhancements (org profiles, pages, wiki v2 API)
Phase 3: Organization Public Profile Page
- Pinned repositories with groups
- Public members display with roles
- API endpoints for pinned repos and groups

Phase 4: Gitea Pages Foundation
- Landing page templates (simple, docs, product, portfolio)
- Custom domain support with verification
- YAML configuration parser (.gitea/landing.yaml)
- Repository settings UI for pages

Phase 5: Enhanced Wiki System with V2 API
- Full CRUD operations via v2 API
- Full-text search with WikiIndex table
- Link graph visualization
- Wiki health metrics (orphaned, dead links, outdated)
- Designed for external AI plugin integration
- Developer guide for .NET integration

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-09 15:14:27 -05:00

313 lines
12 KiB
Go

// 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
}