# Error Codes Reference

Complete reference of error codes, HTTP status codes, and error handling patterns in Izri.

## 📋 Overview

All API errors follow consistent patterns for predictable error handling across the platform.

**Error Format**:

```typescript
{
  code: string           // Error code
  message: string        // Human-readable message
  data?: any            // Additional context
  path?: string         // tRPC procedure path
  statusCode?: number   // HTTP status code
}
```

---

## 🔴 Authentication Errors

### UNAUTHORIZED (401)

**Description**: No valid authentication credentials provided.

**HTTP Status**: 401

**Common Causes**:

- No session cookie
- Expired session
- Invalid session token
- No API token provided

**Examples**:

```json
{
  "code": "UNAUTHORIZED",
  "message": "Not authenticated"
}
```

```json
{
  "code": "UNAUTHORIZED",
  "message": "Session expired"
}
```

```json
{
  "code": "UNAUTHORIZED",
  "message": "Invalid or expired API token"
}
```

**Client Handling**:

```typescript
if (error.code === 'UNAUTHORIZED') {
  // Clear local state
  queryClient.clear()
  
  // Redirect to login
  window.location.href = '/login'
}
```

---

### FORBIDDEN (403)

**Description**: Authenticated but insufficient permissions.

**HTTP Status**: 403

**Common Causes**:

- Insufficient role (e.g., MEMBER trying to delete organization)
- API token with limited scopes
- Accessing resource owned by different user/org

**Examples**:

```json
{
  "code": "FORBIDDEN",
  "message": "Only organization owners can delete organizations"
}
```

```json
{
  "code": "FORBIDDEN",
  "message": "Insufficient permissions to access this resource"
}
```

**Client Handling**:

```typescript
if (error.code === 'FORBIDDEN') {
  toast.error('You don\'t have permission to perform this action')
}
```

---

### SESSION_EXPIRED

**Description**: Session was valid but has now expired.

**HTTP Status**: 401

**Example**:

```json
{
  "code": "SESSION_EXPIRED",
  "message": "Your session has expired. Please log in again."
}
```

**Client Handling**: Same as UNAUTHORIZED

---

## 🔍 Resource Errors

### NOT_FOUND (404)

**Description**: Requested resource doesn't exist or user doesn't have access.

**HTTP Status**: 404

**Common Causes**:

- Invalid resource ID
- Resource deleted
- User doesn't have access (privacy)

**Examples**:

```json
{
  "code": "NOT_FOUND",
  "message": "User not found"
}
```

```json
{
  "code": "NOT_FOUND",
  "message": "Project not found"
}
```

```json
{
  "code": "NOT_FOUND",
  "message": "Organization not found"
}
```

**Client Handling**:

```typescript
if (error.code === 'NOT_FOUND') {
  return <NotFoundPage />
}
```

---

### CONFLICT (409)

**Description**: Request conflicts with existing resource state.

**HTTP Status**: 409

**Common Causes**:

- Duplicate unique field (email, slug)
- Resource already exists
- Concurrent modification

**Examples**:

```json
{
  "code": "CONFLICT",
  "message": "Project with this repository already exists"
}
```

```json
{
  "code": "CONFLICT",
  "message": "An organization with this slug already exists"
}
```

```json
{
  "code": "CONFLICT",
  "message": "User is already a member of this organization"
}
```

**Client Handling**:

```typescript
if (error.code === 'CONFLICT') {
  setFieldError('repository', error.message)
}
```

---

## ✅ Validation Errors

### BAD_REQUEST (400)

**Description**: Invalid request data or parameters.

**HTTP Status**: 400

**Common Causes**:

- Missing required field
- Invalid format (email, URL)
- Out of range value
- Zod validation failure

**Examples**:

```json
{
  "code": "BAD_REQUEST",
  "message": "Invalid input: name is required",
  "data": {
    "zodError": {
      "fieldErrors": {
        "name": ["Required"]
      }
    }
  }
}
```

```json
{
  "code": "BAD_REQUEST",
  "message": "Invalid email format"
}
```

**Client Handling**:

```typescript
if (error.code === 'BAD_REQUEST' && error.data?.zodError) {
  const fieldErrors = error.data.zodError.fieldErrors
  
  Object.entries(fieldErrors).forEach(([field, messages]) => {
    setFieldError(field, messages[0])
  })
}
```

---

### VALIDATION_ERROR

**Description**: Business logic validation failed.

**HTTP Status**: 400

**Examples**:

```json
{
  "code": "VALIDATION_ERROR",
  "message": "Cannot remove the last owner of an organization"
}
```

```json
{
  "code": "VALIDATION_ERROR",
  "message": "Slug must contain only lowercase letters, numbers, and hyphens"
}
```

---

## ⚠️ Business Logic Errors

### PRECONDITION_FAILED (412)

**Description**: Request cannot be completed due to current state.

**HTTP Status**: 412

**Examples**:

```json
{
  "code": "PRECONDITION_FAILED",
  "message": "Cannot change your own role to prevent lockout"
}
```

```json
{
  "code": "PRECONDITION_FAILED",
  "message": "Project must be analyzed before generating tests"
}
```

---

### UNPROCESSABLE_ENTITY (422)

**Description**: Request is well-formed but semantically incorrect.

**HTTP Status**: 422

**Examples**:

```json
{
  "code": "UNPROCESSABLE_ENTITY",
  "message": "GitHub token is required for private repositories"
}
```

```json
{
  "code": "UNPROCESSABLE_ENTITY",
  "message": "Test execution failed: No test files found"
}
```

---

## 🚫 Rate Limiting

### TOO_MANY_REQUESTS (429)

**Description**: Rate limit exceeded.

**HTTP Status**: 429

**Example**:

```json
{
  "code": "TOO_MANY_REQUESTS",
  "message": "Rate limit exceeded. Try again in 60 seconds.",
  "data": {
    "limit": 100,
    "remaining": 0,
    "resetAt": "2024-01-01T00:01:00.000Z"
  }
}
```

**Headers**:

```
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1704067260
Retry-After: 60
```

**Client Handling**:

```typescript
if (error.code === 'TOO_MANY_REQUESTS') {
  const retryAfter = error.data?.resetAt
  toast.error(`Rate limit exceeded. Try again at ${formatTime(retryAfter)}`)
}
```

---

## 💥 Server Errors

### INTERNAL_SERVER_ERROR (500)

**Description**: Unexpected server error.

**HTTP Status**: 500

**Example**:

```json
{
  "code": "INTERNAL_SERVER_ERROR",
  "message": "An unexpected error occurred",
  "data": {
    "requestId": "req_abc123",
    "timestamp": "2024-01-01T00:00:00.000Z"
  }
}
```

**Client Handling**:

```typescript
if (error.code === 'INTERNAL_SERVER_ERROR') {
  console.error('Request ID:', error.data?.requestId)
  toast.error('Something went wrong. Please try again.')
  
  // Optionally report to error tracking
  Sentry.captureException(error, {
    extra: { requestId: error.data?.requestId }
  })
}
```

---

### SERVICE_UNAVAILABLE (503)

**Description**: Service temporarily unavailable.

**HTTP Status**: 503

**Common Causes**:

- Database connection failure
- External service (GitHub API) down
- Maintenance mode

**Example**:

```json
{
  "code": "SERVICE_UNAVAILABLE",
  "message": "Service temporarily unavailable",
  "data": {
    "retryAfter": 300
  }
}
```

---

### TIMEOUT (408)

**Description**: Request took too long to process.

**HTTP Status**: 408

**Example**:

```json
{
  "code": "TIMEOUT",
  "message": "Request timeout - repository analysis is taking longer than expected",
  "data": {
    "timeoutMs": 60000
  }
}
```

---

## 🔧 Integration Errors

### EXTERNAL_SERVICE_ERROR

**Description**: External service (GitHub, AI) failed.

**HTTP Status**: 502

**Examples**:

```json
{
  "code": "EXTERNAL_SERVICE_ERROR",
  "message": "GitHub API error: Repository not found",
  "data": {
    "service": "github",
    "statusCode": 404
  }
}
```

```json
{
  "code": "EXTERNAL_SERVICE_ERROR",
  "message": "AI service unavailable",
  "data": {
    "service": "openai",
    "error": "Rate limit exceeded"
  }
}
```

---

## 📊 Error Handling Patterns

### Frontend Global Error Handler

```typescript
import { TRPCClientError } from '@trpc/client'
import type { AppRouter } from '@izri/trpc'

export function handleTRPCError(error: unknown) {
  if (!(error instanceof TRPCClientError)) {
    console.error('Unknown error:', error)
    return
  }
  
  const trpcError = error as TRPCClientError<AppRouter>
  const code = trpcError.data?.code
  const message = trpcError.message
  
  switch (code) {
    case 'UNAUTHORIZED':
      queryClient.clear()
      window.location.href = '/login'
      break
      
    case 'FORBIDDEN':
      toast.error('You don\'t have permission for this action')
      break
      
    case 'NOT_FOUND':
      toast.error('Resource not found')
      break
      
    case 'CONFLICT':
      toast.error(message)
      break
      
    case 'BAD_REQUEST':
      // Handle validation errors
      if (trpcError.data?.zodError) {
        const fieldErrors = trpcError.data.zodError.fieldErrors
        Object.entries(fieldErrors).forEach(([field, messages]) => {
          console.error(`${field}: ${messages[0]}`)
        })
      }
      break
      
    case 'INTERNAL_SERVER_ERROR':
      toast.error('Something went wrong. Please try again.')
      Sentry.captureException(error)
      break
      
    default:
      toast.error(message || 'An error occurred')
  }
}
```

---

### Mutation Error Handling

```typescript
const { mutate } = trpc.projects.createProject.useMutation({
  onError: (error) => {
    if (error.data?.code === 'CONFLICT') {
      setError('repository', {
        type: 'manual',
        message: 'This repository is already added'
      })
    } else if (error.data?.zodError) {
      const fieldErrors = error.data.zodError.fieldErrors
      Object.entries(fieldErrors).forEach(([field, messages]) => {
        setError(field as any, {
          type: 'manual',
          message: messages[0]
        })
      })
    } else {
      toast.error(error.message)
    }
  },
  onSuccess: (data) => {
    toast.success('Project created!')
    navigate(`/projects/${data.project.id}`)
  }
})
```

---

### Query Error Handling

```typescript
const { data, error, isError } = trpc.projects.getProject.useQuery(
  { projectId: 'proj_123', userEmail: user.email },
  {
    retry: (failureCount, error) => {
      // Don't retry on client errors
      if (error.data?.code === 'NOT_FOUND') return false
      if (error.data?.code === 'FORBIDDEN') return false
      
      // Retry server errors up to 3 times
      return failureCount < 3
    }
  }
)

if (isError) {
  if (error.data?.code === 'NOT_FOUND') {
    return <NotFoundPage />
  }
  
  return <ErrorBoundary error={error} />
}
```

---

### Backend Error Throwing

```typescript
import { TRPCError } from '@trpc/server'

export const projectsRouter = router({
  getProject: protectedProcedure
    .input(z.object({ projectId: z.string() }))
    .query(async ({ ctx, input }) => {
      const project = await db.query.projects.findFirst({
        where: eq(projects.id, input.projectId)
      })
      
      if (!project) {
        throw new TRPCError({
          code: 'NOT_FOUND',
          message: 'Project not found'
        })
      }
      
      if (project.userId !== ctx.user.id) {
        throw new TRPCError({
          code: 'FORBIDDEN',
          message: 'You don\'t have access to this project'
        })
      }
      
      return { project }
    })
})
```

---

## 🔗 HTTP Status Code Reference

| Code | Name | tRPC Code | Usage |
|------|------|-----------|-------|
| 200 | OK | - | Successful request |
| 201 | Created | - | Resource created |
| 400 | Bad Request | BAD_REQUEST | Validation error |
| 401 | Unauthorized | UNAUTHORIZED | Not authenticated |
| 403 | Forbidden | FORBIDDEN | Insufficient permissions |
| 404 | Not Found | NOT_FOUND | Resource not found |
| 408 | Timeout | TIMEOUT | Request timeout |
| 409 | Conflict | CONFLICT | Resource conflict |
| 412 | Precondition Failed | PRECONDITION_FAILED | State validation failed |
| 422 | Unprocessable | UNPROCESSABLE_ENTITY | Semantic error |
| 429 | Too Many Requests | TOO_MANY_REQUESTS | Rate limit exceeded |
| 500 | Internal Error | INTERNAL_SERVER_ERROR | Server error |
| 502 | Bad Gateway | EXTERNAL_SERVICE_ERROR | External service failed |
| 503 | Unavailable | SERVICE_UNAVAILABLE | Service down |

---

## 🔗 Related Documentation

- [tRPC Routers](./trpc-routers.md) - API endpoints
- [REST Endpoints](./rest-endpoints.md) - REST API errors
- [Authentication API](./authentication-api.md) - Auth errors
- [Backend Error Handling](../backend/error-handling.md) - Server-side error handling
- [Frontend Error Handling](../frontend/components.md) - UI error boundaries
