# Error Handling System - Developer Documentation

**Version:** 1.0.0
**Last Updated:** 2025
**Status:** Phase 1 Complete (Admin), Phase 2-3 Pending (Store, Storefront)

---

## Table of Contents

1. [Overview](#overview)
2. [Architecture](#architecture)
3. [System Components](#system-components)
4. [Context Detection](#context-detection)
5. [Error Response Types](#error-response-types)
6. [Template System](#template-system)
7. [Implementation Status](#implementation-status)
8. [Developer Guidelines](#developer-guidelines)
9. [Adding New Error Pages](#adding-new-error-pages)
10. [Testing Guide](#testing-guide)
11. [Troubleshooting](#troubleshooting)
12. [API Reference](#api-reference)

---

## Overview

### Purpose

The error handling system provides context-aware error responses throughout the application. It automatically determines whether to return JSON (for API calls) or HTML error pages (for browser requests), and renders appropriate error pages based on the request context (Admin, Store Dashboard, or Storefront).

### Key Features

- **Context-Aware**: Different error pages for Admin, Store, and Storefront areas
- **Automatic Detection**: Distinguishes between API and HTML page requests
- **Consistent JSON API**: API endpoints always return standardized JSON errors
- **Fallback Mechanism**: Gracefully handles missing templates
- **Debug Mode**: Shows technical details to admin users only
- **Theme Integration**: Storefront error pages support store theming (Phase 3)
- **Security**: 401 errors automatically redirect to appropriate login pages

### Design Principles

1. **Separation of Concerns**: HTML templates are separate from exception handler logic
2. **Fail-Safe**: Multiple fallback levels ensure errors always render
3. **Developer-Friendly**: Easy to add new error pages or contexts
4. **User-Centric**: Professional error pages with clear messaging and actions
5. **Secure**: Technical details hidden from non-admin users

---

## Architecture

### Request Flow

```
HTTP Request
    ↓
Store Context Middleware (detects store from domain/subdomain/path)
    ↓
Context Detection Middleware (detects API/Admin/Store/Storefront)
    ↓
Route Handler (processes request, may throw exception)
    ↓
Exception Handler (catches and processes exception)
    ↓
    ├─→ API Request? → Return JSON Response
    ├─→ HTML Request + 401? → Redirect to Login
    └─→ HTML Request? → Render Context-Aware Error Page
```

### Components

```
middleware/
├── store_context.py      # Detects store from URL (existing)
└── context_middleware.py  # Detects request context type (NEW)

app/exceptions/
├── handler.py             # Exception handlers (refactored)
├── error_renderer.py      # Error page rendering logic (NEW)
└── base.py               # Base exception classes (existing)

app/templates/
├── admin/errors/         # Admin error pages (Phase 1 - COMPLETE)
├── store/errors/        # Store error pages (Phase 2 - PENDING)
├── storefront/errors/    # Storefront error pages (Phase 3 - PENDING)
└── shared/               # Shared fallback error pages (COMPLETE)
```

---

## System Components

### 1. Context Detection Middleware

**File:** `middleware/context_middleware.py`

**Purpose:** Detects the request context type and injects it into `request.state.context_type`.

**Context Types:**

```python
class RequestContext(str, Enum):
    API = "api"                    # API endpoints (/api/*)
    ADMIN = "admin"                # Admin portal (/admin/* or admin.*)
    STORE_DASHBOARD = "store"    # Store management (/store/*)
    SHOP = "shop"                  # Customer storefront (store subdomains)
    FALLBACK = "fallback"          # Unknown/generic context
```

**Detection Logic:**

1. **API** - Path starts with `/api/` (highest priority)
2. **ADMIN** - Path starts with `/admin` or host starts with `admin.`
3. **STORE_DASHBOARD** - Path starts with `/store/`
4. **SHOP** - `request.state.store` exists (set by store_context_middleware)
5. **FALLBACK** - None of the above match

**Usage:**

```python
from middleware.frontend_type import get_frontend_type
from app.modules.enums import FrontendType

def my_handler(request: Request):
    frontend_type = get_frontend_type(request)

    if frontend_type == FrontendType.ADMIN:
        # Handle admin-specific logic
        pass
```

### 2. Error Page Renderer

**File:** `app/exceptions/error_renderer.py`

**Purpose:** Renders context-aware HTML error pages using Jinja2 templates.

**Key Methods:**

```python
ErrorPageRenderer.render_error_page(
    request: Request,
    status_code: int,
    error_code: str,
    message: str,
    details: Optional[Dict[str, Any]] = None,
    show_debug: bool = False,
) -> HTMLResponse
```

**Template Selection Priority:**

1. `{context}/errors/{status_code}.html` (e.g., `admin/errors/404.html`)
2. `{context}/errors/generic.html` (e.g., `admin/errors/generic.html`)
3. `shared/{status_code}-fallback.html` (e.g., `shared/404-fallback.html`)
4. `shared/generic-fallback.html` (absolute fallback)

**Template Variables Provided:**

```python
{
    "status_code": int,           # HTTP status code
    "status_name": str,           # Friendly name ("Not Found")
    "error_code": str,            # Application error code
    "message": str,               # User-friendly message
    "details": dict,              # Additional error details
    "show_debug": bool,           # Whether to show debug info
    "context_type": str,          # Request context type
    "path": str,                  # Request path
    "store": dict,               # Store info (storefront context only)
    "theme": dict,                # Theme data (storefront context only)
}
```

### 3. Exception Handler

**File:** `app/exceptions/handler.py`

**Purpose:** Central exception handling for all application exceptions.

**Handlers:**

- `OrionException` - Custom application exceptions
- `HTTPException` - FastAPI HTTP exceptions
- `RequestValidationError` - Pydantic validation errors
- `Exception` - Generic Python exceptions
- `404` - Not Found errors

**Response Logic:**

```python
if request.url.path.startswith("/api/"):
    return JSONResponse(...)  # Always JSON for API
elif _is_html_page_request(request):
    if status_code == 401:
        return RedirectResponse(...)  # Redirect to login
    else:
        return ErrorPageRenderer.render_error_page(...)  # HTML error page
else:
    return JSONResponse(...)  # Default to JSON
```

---

## Context Detection

### How Context is Determined

The system uses a priority-based approach to detect context:

```python
# Priority 1: API Context
if path.startswith("/api/"):
    return RequestContext.API

# Priority 2: Admin Context
if path.startswith("/admin") or host.startswith("admin."):
    return RequestContext.ADMIN

# Priority 3: Store Dashboard Context
if path.startswith("/store/"):
    return RequestContext.STORE_DASHBOARD

# Priority 4: Storefront Context
if hasattr(request.state, 'store') and request.state.store:
    return RequestContext.SHOP

# Priority 5: Fallback
return RequestContext.FALLBACK
```

### Context Examples

| URL | Host | Context |
|-----|------|---------|
| `/api/v1/admin/stores` | any | API |
| `/admin/dashboard` | any | ADMIN |
| `/store/products` | any | STORE_DASHBOARD |
| `/products` | `store1.platform.com` | SHOP |
| `/products` | `customdomain.com` | SHOP (if store detected) |
| `/about` | `platform.com` | FALLBACK |

### Special Cases

**Admin Access via Subdomain:**
```
admin.platform.com/dashboard → ADMIN context
```

**Store Dashboard Access:**
```
/store/store1/dashboard → STORE_DASHBOARD context
store1.platform.com/store/dashboard → STORE_DASHBOARD context
```

**Storefront Access:**
```
store1.platform.com/ → SHOP context
customdomain.com/ → SHOP context (if store verified)
```

---

## Error Response Types

### 1. JSON Responses (API Context)

**When:** Request path starts with `/api/`

**Format:**
```json
{
  "error_code": "STORE_NOT_FOUND",
  "message": "Store with ID '999' not found",
  "status_code": 404,
  "details": {
    "store_id": "999"
  }
}
```

**Always JSON, Regardless of Accept Header:**
API endpoints MUST always return JSON, even if the client sends `Accept: text/html`.

### 2. HTML Error Pages (HTML Page Requests)

**When:**
- NOT an API request
- GET request
- Accept header includes `text/html`
- NOT already on login page

**Renders:** Context-appropriate HTML error page

**Example:** Admin 404
```html
<html>
  <head><title>404 - Page Not Found | Admin Portal</title></head>
  <body>
    <div class="error-container">
      <h1>404</h1>
      <p>The admin page you're looking for doesn't exist.</p>
      <a href="/admin/dashboard">Go to Dashboard</a>
    </div>
  </body>
</html>
```

### 3. Login Redirects (401 Unauthorized)

**When:**
- HTML page request
- 401 status code

**Behavior:** Redirect to appropriate login page based on context

| Context | Redirect To |
|---------|-------------|
| ADMIN | `/admin/login` |
| STORE_DASHBOARD | `/store/login` |
| SHOP | `/storefront/login` |
| FALLBACK | `/admin/login` |

---

## Template System

### Template Structure

All error templates follow this organization:

```
app/templates/
├── admin/
│   └── errors/
│       ├── base.html          # Base template (extends nothing)
│       ├── 400.html           # Bad Request
│       ├── 401.html           # Unauthorized
│       ├── 403.html           # Forbidden
│       ├── 404.html           # Not Found
│       ├── 422.html           # Validation Error
│       ├── 429.html           # Rate Limit
│       ├── 500.html           # Internal Server Error
│       ├── 502.html           # Bad Gateway
│       └── generic.html       # Catch-all
│
├── store/
│   └── errors/
│       └── (same structure as admin)
│
├── storefront/
│   └── errors/
│       └── (same structure as admin)
│
└── shared/
    ├── 404-fallback.html
    ├── 500-fallback.html
    ├── generic-fallback.html
    └── cdn-fallback.html
```

### Base Template Pattern

Each context has its own `base.html` template:

```html
<!-- app/templates/admin/errors/base.html -->
<!DOCTYPE html>
<html lang="en">
<head>
    <title>{% block title %}{{ status_code }} - {{ status_name }}{% endblock %}</title>
    <style>
        /* Admin-specific styling */
    </style>
</head>
<body>
    <div class="error-container">
        {% block content %}
        <!-- Default error content -->
        {% endblock %}
    </div>
</body>
</html>
```

### Specific Error Template Pattern

```html
<!-- app/templates/admin/errors/404.html -->
{% extends "admin/errors/base.html" %}

{% block icon %}🔍{% endblock %}
{% block title %}404 - Page Not Found{% endblock %}

{% block content %}
<div class="error-icon">🔍</div>
<div class="status-code">404</div>
<div class="status-name">Page Not Found</div>
<div class="error-message">
    The admin page you're looking for doesn't exist or has been moved.
</div>
<div class="error-code">Error Code: {{ error_code }}</div>

<div class="action-buttons">
    <a href="/admin/dashboard" class="btn btn-primary">Go to Dashboard</a>
    <a href="javascript:history.back()" class="btn btn-secondary">Go Back</a>
</div>

{% if show_debug %}
<div class="debug-info">
    <!-- Debug information for admins -->
</div>
{% endif %}
{% endblock %}
```

### Context-Specific Considerations

#### Admin Error Pages

**Purpose:** Error pages for platform administrators

**Characteristics:**
- Professional platform branding
- Links to admin dashboard
- Full debug information when appropriate
- Support contact link

**Action Buttons:**
- Primary: "Go to Dashboard" → `/admin/dashboard`
- Secondary: "Go Back" → `javascript:history.back()`

#### Store Error Pages

**Purpose:** Error pages for store dashboard users

**Characteristics:**
- Professional store management branding
- Links to store dashboard
- Debug information for store admins
- Store support contact link

**Action Buttons:**
- Primary: "Go to Dashboard" → `/store/dashboard`
- Secondary: "Go Back" → `javascript:history.back()`

#### Storefront Error Pages

**Purpose:** Error pages for customers on store storefronts

**Characteristics:**
- **Uses store theme** (colors, logo, fonts)
- Customer-friendly language
- No technical jargon
- Links to storefront homepage
- Customer support contact

**Action Buttons:**
- Primary: "Continue Shopping" → Storefront homepage
- Secondary: "Contact Support" → Store support page

**Theme Integration:**
```html
<!-- Storefront error pages use store theme variables -->
<style>
    :root {
        --color-primary: {{ theme.colors.primary }};
        --color-background: {{ theme.colors.background }};
        --font-body: {{ theme.fonts.body }};
    }
</style>
```

---

## Implementation Status

### Phase 1: Admin Error Handling ✅ COMPLETE

**Status:** Fully implemented and ready for use

**Components:**
- ✅ Context detection middleware
- ✅ Error page renderer
- ✅ Refactored exception handler
- ✅ Admin error templates (11 files)
- ✅ Fallback templates (3 files)

**Error Codes Implemented:**
- ✅ 400 (Bad Request)
- ✅ 401 (Unauthorized)
- ✅ 403 (Forbidden)
- ✅ 404 (Not Found)
- ✅ 422 (Validation Error)
- ✅ 429 (Rate Limit)
- ✅ 500 (Internal Server Error)
- ✅ 502 (Bad Gateway)
- ✅ Generic (Catch-all)

### Phase 2: Store Error Handling ⏳ PENDING

**Status:** Not yet implemented

**Required Tasks:**
1. Create `/app/templates/store/errors/` directory
2. Copy admin templates as starting point
3. Customize messaging for store context:
   - Change "Admin Portal" to "Store Portal"
   - Update dashboard links to `/store/dashboard`
   - Adjust support links to store support
4. Update action button destinations
5. Test all error codes in store context

**Estimated Effort:** 1-2 hours

**Priority:** Medium (stores currently see fallback pages)

### Phase 3: Storefront Error Handling ⏳ PENDING

**Status:** Not yet implemented

**Required Tasks:**
1. Create `/app/templates/storefront/errors/` directory
2. Create customer-facing error templates:
   - Use customer-friendly language
   - Integrate store theme variables
   - Add store logo/branding
3. Update ErrorPageRenderer to pass theme data
4. Implement theme integration:
   ```python
   if context_type == RequestContext.SHOP:
       template_data["theme"] = request.state.theme
       template_data["store"] = request.state.store
   ```
5. Test with multiple store themes
6. Test on custom domains

**Estimated Effort:** 2-3 hours

**Priority:** High (customers currently see non-branded fallback pages)

**Dependencies:** Requires store theme system (already exists)

---

## Developer Guidelines

### When to Throw Exceptions

**Use Custom Exceptions:**
```python
from app.exceptions import StoreNotFoundException

# Good - Specific exception
raise StoreNotFoundException(store_id="123")

# Avoid - Generic exception with less context
raise HTTPException(status_code=404, detail="Store not found")
```

**Exception Selection Guide:**

| Scenario | Exception | Status Code |
|----------|-----------|-------------|
| Resource not found | `{Resource}NotFoundException` | 404 |
| Validation failed | `ValidationException` | 422 |
| Unauthorized | `AuthenticationException` | 401 |
| Forbidden | `AuthorizationException` | 403 |
| Business rule violated | `BusinessLogicException` | 400 |
| External service failed | `ExternalServiceException` | 502 |
| Rate limit exceeded | `RateLimitException` | 429 |

### Creating Custom Exceptions

Follow this pattern:

```python
# app/exceptions/my_domain.py
from .base import ResourceNotFoundException

class MyResourceNotFoundException(ResourceNotFoundException):
    """Raised when MyResource is not found."""

    def __init__(self, resource_id: str):
        super().__init__(
            resource_type="MyResource",
            identifier=resource_id,
            message=f"MyResource with ID '{resource_id}' not found",
            error_code="MY_RESOURCE_NOT_FOUND",
        )
```

Then register in `app/exceptions/__init__.py`:

```python
from .my_domain import MyResourceNotFoundException

__all__ = [
    # ... existing exports
    "MyResourceNotFoundException",
]
```

### Error Messages Best Practices

**User-Facing Messages:**
- Clear and concise
- Avoid technical jargon
- Suggest next actions
- Never expose sensitive information

```python
# Good
message="The product you're looking for is currently unavailable."

# Bad
message="SELECT * FROM products WHERE id=123 returned 0 rows"
```

**Error Codes:**
- Use UPPER_SNAKE_CASE
- Be descriptive but concise
- Follow existing patterns

```python
# Good
error_code="PRODUCT_OUT_OF_STOCK"
error_code="PAYMENT_PROCESSING_FAILED"

# Bad
error_code="error1"
error_code="ProductOutOfStockException"
```

### Details Dictionary

Use the `details` dictionary for additional context:

```python
raise StoreNotFoundException(
    store_id="123"
)
# Results in:
# details = {"store_id": "123", "resource_type": "Store"}

# For validation errors:
raise ValidationException(
    message="Invalid email format",
    field="email",
    details={
        "provided_value": user_input,
        "expected_format": "user@example.com"
    }
)
```

**What to Include in Details:**
- ✅ Resource identifiers
- ✅ Validation error specifics
- ✅ Operation context
- ✅ Non-sensitive debugging info
- ❌ Passwords or secrets
- ❌ Internal system paths
- ❌ Database queries
- ❌ Stack traces (use show_debug instead)

---

## Adding New Error Pages

### Step 1: Identify Context

Determine which context needs the new error page:
- Admin Portal → `admin/errors/`
- Store Dashboard → `store/errors/`
- Customer Storefront → `storefront/errors/`

### Step 2: Choose Template Type

**Specific Error Code Template:**
- For common HTTP status codes (400, 403, 404, 500, etc.)
- File name: `{status_code}.html`

**Generic Catch-All Template:**
- For less common or custom error codes
- File name: `generic.html`
- Uses variables to display appropriate content

### Step 3: Create Template

**Option A: Extend Base Template** (Recommended)

```html
<!-- app/templates/admin/errors/503.html -->
{% extends "admin/errors/base.html" %}

{% block icon %}🔧{% endblock %}
{% block title %}503 - Service Unavailable{% endblock %}

{% block content %}
<div class="error-icon">🔧</div>
<div class="status-code">503</div>
<div class="status-name">Service Unavailable</div>
<div class="error-message">
    The service is temporarily unavailable. We're working to restore it.
</div>
<div class="error-code">Error Code: {{ error_code }}</div>

<div class="action-buttons">
    <a href="javascript:location.reload()" class="btn btn-primary">Retry</a>
    <a href="/admin/dashboard" class="btn btn-secondary">Dashboard</a>
</div>

{% if show_debug %}
<div class="debug-info">
    <h3>🔧 Debug Information (Admin Only)</h3>
    <div class="debug-item">
        <span class="debug-label">Path:</span>
        <span class="debug-value">{{ path }}</span>
    </div>
    {% if details %}
    <div class="debug-item">
        <span class="debug-label">Details:</span>
        <pre>{{ details | tojson(indent=2) }}</pre>
    </div>
    {% endif %}
</div>
{% endif %}
{% endblock %}
```

**Option B: Standalone Template** (For custom designs)

```html
<!-- app/templates/storefront/errors/custom.html -->
<!DOCTYPE html>
<html lang="en">
<head>
    <title>{{ status_code }} - {{ status_name }}</title>
    <style>
        /* Custom styling that uses store theme */
        :root {
            --primary: {{ theme.colors.primary }};
        }
    </style>
</head>
<body>
    <!-- Custom error page design -->
</body>
</html>
```

### Step 4: Add to ErrorPageRenderer (if needed)

If adding a new status code with a friendly name:

```python
# app/exceptions/error_renderer.py

STATUS_CODE_NAMES = {
    # ... existing codes
    503: "Service Unavailable",  # Add new code
}

STATUS_CODE_MESSAGES = {
    # ... existing messages
    503: "The service is temporarily unavailable. Please try again later.",
}
```

### Step 5: Test

```bash
# Test the new error page
curl -H "Accept: text/html" http://localhost:8000/admin/test-503

# Or trigger programmatically:
from app.exceptions import ServiceUnavailableException
raise ServiceUnavailableException("Maintenance in progress")
```

---

## Testing Guide

### Unit Tests

**Test Context Detection:**

```python
# tests/test_frontend_detector.py
from app.core.frontend_detector import FrontendDetector
from app.modules.enums import FrontendType

def test_admin_detection():
    assert FrontendDetector.is_admin("localhost", "/admin/dashboard") is True

def test_storefront_detection():
    frontend_type = FrontendDetector.detect(
        host="localhost", path="/storefront/products", has_store_context=True
    )
    assert frontend_type == FrontendType.STOREFRONT
```

**Test Error Renderer:**

```python
# tests/test_error_renderer.py
from app.exceptions.error_renderer import ErrorPageRenderer

def test_template_selection_admin():
    template = ErrorPageRenderer._find_template(
        context_type=RequestContext.ADMIN,
        status_code=404
    )
    assert template == "admin/errors/404.html"

def test_template_fallback():
    template = ErrorPageRenderer._find_template(
        context_type=RequestContext.ADMIN,
        status_code=999  # Non-existent error code
    )
    assert template == "admin/errors/generic.html"
```

**Test Exception Handlers:**

```python
# tests/test_exception_handlers.py
from fastapi.testclient import TestClient

def test_api_returns_json(client: TestClient):
    response = client.get("/api/v1/nonexistent")
    assert response.status_code == 404
    assert response.headers["content-type"] == "application/json"
    data = response.json()
    assert "error_code" in data
    assert "message" in data

def test_html_page_returns_html(client: TestClient):
    response = client.get(
        "/admin/nonexistent",
        headers={"Accept": "text/html"}
    )
    assert response.status_code == 404
    assert "text/html" in response.headers["content-type"]
    assert "<html" in response.text
```

### Integration Tests

**Test Admin Error Pages:**

```python
def test_admin_404_error_page(client: TestClient):
    response = client.get(
        "/admin/nonexistent",
        headers={"Accept": "text/html"}
    )
    assert response.status_code == 404
    assert "Page Not Found" in response.text
    assert "/admin/dashboard" in response.text  # Dashboard link
    assert "Admin Portal" in response.text

def test_admin_403_error_page(client: TestClient):
    # Trigger 403 by accessing restricted resource
    response = client.get(
        "/admin/super-admin-only",
        headers={"Accept": "text/html", "Authorization": "Bearer regular_user_token"}
    )
    assert response.status_code == 403
    assert "Access Denied" in response.text
```

**Test 401 Redirect:**

```python
def test_401_redirects_to_admin_login(client: TestClient):
    response = client.get(
        "/admin/dashboard",
        headers={"Accept": "text/html"},
        follow_redirects=False
    )
    assert response.status_code == 302
    assert response.headers["location"] == "/admin/login"

def test_401_api_returns_json(client: TestClient):
    response = client.get(
        "/api/v1/admin/dashboard",
        follow_redirects=False
    )
    assert response.status_code == 401
    assert response.headers["content-type"] == "application/json"
```

### Manual Testing Checklist

**Admin Context:**

```bash
# Test 404
curl -H "Accept: text/html" http://localhost:8000/admin/nonexistent
# Expected: Admin 404 page with "Go to Dashboard" button

# Test API 404
curl http://localhost:8000/api/v1/admin/nonexistent
# Expected: JSON error response

# Test 401 redirect
curl -H "Accept: text/html" http://localhost:8000/admin/dashboard
# Expected: 302 redirect to /admin/login

# Test validation error
curl -X POST -H "Accept: text/html" http://localhost:8000/admin/stores \
  -d '{"invalid": "data"}'
# Expected: Admin 422 page with validation errors
```

**Store Context (Phase 2):**

```bash
# Test 404
curl -H "Accept: text/html" http://localhost:8000/store/nonexistent
# Expected: Store 404 page

# Test 401 redirect
curl -H "Accept: text/html" http://localhost:8000/store/dashboard
# Expected: 302 redirect to /store/login
```

**Storefront Context (Phase 3):**

```bash
# Test 404 on store subdomain
curl -H "Accept: text/html" http://store1.localhost:8000/nonexistent
# Expected: Storefront 404 page with store theme

# Test 500 error
# Trigger server error on storefront
# Expected: Storefront 500 page with store branding
```

### Performance Testing

**Load Test Error Handling:**

```python
# Test that error pages don't significantly impact performance
import time

def test_error_page_performance(client: TestClient):
    # Test 100 consecutive 404 requests
    start = time.time()
    for _ in range(100):
        response = client.get(
            "/admin/nonexistent",
            headers={"Accept": "text/html"}
        )
        assert response.status_code == 404
    duration = time.time() - start

    # Should complete in reasonable time (< 5 seconds)
    assert duration < 5.0
    print(f"100 error pages rendered in {duration:.2f}s")
```

### Template Validation

**Check Template Syntax:**

```bash
# Validate all Jinja2 templates
python -c "
from jinja2 import Environment, FileSystemLoader
import os

env = Environment(loader=FileSystemLoader('app/templates'))
errors = []

for root, dirs, files in os.walk('app/templates'):
    for file in files:
        if file.endswith('.html'):
            path = os.path.join(root, file)
            rel_path = os.path.relpath(path, 'app/templates')
            try:
                env.get_template(rel_path)
                print(f'✓ {rel_path}')
            except Exception as e:
                errors.append((rel_path, str(e)))
                print(f'✗ {rel_path}: {e}')

if errors:
    print(f'\n{len(errors)} template(s) have errors')
    exit(1)
else:
    print(f'\nAll templates validated successfully')
"
```

---

## Troubleshooting

### Issue: API Returning HTML Instead of JSON

**Symptoms:**
```bash
curl http://localhost:8000/api/v1/admin/stores/999
# Returns HTML instead of JSON
```

**Diagnosis:**
Check if `_is_api_request()` is working correctly:

```python
# Add logging to handler.py
logger.debug(f"Path: {request.url.path}, is_api: {_is_api_request(request)}")
```

**Solution:**
Ensure path check is correct:
```python
def _is_api_request(request: Request) -> bool:
    return request.url.path.startswith("/api/")  # Must have leading slash
```

### Issue: HTML Pages Returning JSON

**Symptoms:**
Browser shows JSON instead of error page when visiting `/admin/nonexistent`

**Diagnosis:**
Check Accept header:
```python
# Add logging
logger.debug(f"Accept: {request.headers.get('accept', '')}")
logger.debug(f"is_html: {_is_html_page_request(request)}")
```

**Common Causes:**
1. Browser not sending `Accept: text/html`
2. Request is POST/PUT/DELETE (not GET)
3. Path is `/api/*`
4. Already on login page

**Solution:**
Verify `_is_html_page_request()` logic:
```python
def _is_html_page_request(request: Request) -> bool:
    if _is_api_request(request):
        return False
    if request.url.path.endswith("/login"):
        return False
    if request.method != "GET":
        return False
    accept_header = request.headers.get("accept", "")
    if "text/html" not in accept_header:
        return False
    return True
```

### Issue: Template Not Found Error

**Symptoms:**
```
jinja2.exceptions.TemplateNotFound: admin/errors/404.html
```

**Diagnosis:**
1. Check template file exists:
```bash
ls -la app/templates/admin/errors/404.html
```

2. Check templates directory configuration in main.py:
```python
# Should be:
TEMPLATES_DIR = BASE_DIR / "app" / "templates"
templates = Jinja2Templates(directory=str(TEMPLATES_DIR))
```

**Solution:**
- Ensure template files are in correct location
- Verify templates directory path is correct
- Check file permissions

### Issue: Wrong Context Detected

**Symptoms:**
Admin page shows storefront error page, or vice versa

**Diagnosis:**
Add context logging:
```python
# In context_middleware.py
logger.info(
    f"Context detection: path={request.url.path}, "
    f"host={request.headers.get('host')}, "
    f"context={context_type}"
)
```

**Solution:**
Check middleware order in `main.py`:
```python
# Correct order:
app.middleware("http")(store_context_middleware)  # FIRST
app.middleware("http")(context_middleware)         # SECOND
```

### Issue: Debug Info Not Showing for Admin

**Symptoms:**
Debug information not visible in admin error pages

**Diagnosis:**
Check `_is_admin_user()` implementation:
```python
# In error_renderer.py
@staticmethod
def _is_admin_user(request: Request) -> bool:
    context_type = get_request_context(request)
    logger.debug(f"Checking admin user: context={context_type}")
    return context_type == RequestContext.ADMIN
```

**Solution:**
- Verify context is correctly detected as ADMIN
- Check `show_debug` parameter is True in render call
- Ensure template has `{% if show_debug %}` block

### Issue: 401 Not Redirecting

**Symptoms:**
401 errors show JSON instead of redirecting to login

**Diagnosis:**
Check if request is detected as HTML page request:
```python
# Add logging before redirect check
logger.debug(
    f"401 handling: is_html={_is_html_page_request(request)}, "
    f"path={request.url.path}, method={request.method}, "
    f"accept={request.headers.get('accept')}"
)
```

**Solution:**
Ensure all conditions for HTML page request are met:
- NOT an API endpoint
- GET request
- Accept header includes `text/html`
- NOT already on login page

### Issue: Store Theme Not Applied to Storefront Errors

**Symptoms:**
Storefront error pages don't use store colors/branding (Phase 3 issue)

**Diagnosis:**
1. Check if theme is in request state:
```python
logger.debug(f"Theme in state: {hasattr(request.state, 'theme')}")
logger.debug(f"Theme data: {getattr(request.state, 'theme', None)}")
```

2. Check if theme is passed to template:
```python
# In error_renderer.py _get_context_data()
if context_type == RequestContext.SHOP:
    theme = getattr(request.state, "theme", None)
    logger.debug(f"Passing theme to template: {theme}")
```

**Solution:**
- Ensure theme_context_middleware ran before error
- Verify theme data structure is correct
- Check template uses theme variables correctly:
```html
<style>
:root {
    --primary: {{ theme.colors.primary if theme else '#6366f1' }};
}
</style>
```

---

## API Reference

### Frontend Type Detection

#### `get_frontend_type(request: Request) -> FrontendType`

Gets the frontend type for the current request.

**Parameters:**
- `request` (Request): FastAPI request object

**Returns:**
- FrontendType enum value

**Example:**
```python
from middleware.frontend_type import get_frontend_type
from app.modules.enums import FrontendType

def my_handler(request: Request):
    frontend_type = get_frontend_type(request)
    return {"frontend_type": frontend_type.value}
```

### Error Rendering

#### `ErrorPageRenderer.render_error_page(...) -> HTMLResponse`

Renders context-aware HTML error page.

**Parameters:**
```python
request: Request                          # FastAPI request object
status_code: int                          # HTTP status code
error_code: str                           # Application error code
message: str                              # User-friendly error message
details: Optional[Dict[str, Any]] = None  # Additional error details
show_debug: bool = False                  # Whether to show debug info
```

**Returns:**
- HTMLResponse with rendered error page

**Example:**
```python
from app.exceptions.error_renderer import ErrorPageRenderer

return ErrorPageRenderer.render_error_page(
    request=request,
    status_code=404,
    error_code="STORE_NOT_FOUND",
    message="The store you're looking for doesn't exist.",
    details={"store_id": "123"},
    show_debug=True,
)
```

#### `ErrorPageRenderer.get_templates_dir() -> Path`

Gets the templates directory path.

**Returns:**
- Path object pointing to templates directory

#### `ErrorPageRenderer._find_template(context_type, status_code) -> str`

Finds appropriate error template based on context and status code.

**Parameters:**
- `context_type` (RequestContext): Request context type
- `status_code` (int): HTTP status code

**Returns:**
- String path to template (e.g., "admin/errors/404.html")

### Exception Utilities

#### `raise_not_found(resource_type: str, identifier: str) -> None`

Convenience function to raise ResourceNotFoundException.

**Example:**
```python
from app.exceptions.handler import raise_not_found

raise_not_found("Store", "123")
# Raises: ResourceNotFoundException with appropriate message
```

#### `raise_validation_error(message: str, field: str = None, details: dict = None) -> None`

Convenience function to raise ValidationException.

**Example:**
```python
from app.exceptions.handler import raise_validation_error

raise_validation_error(
    message="Invalid email format",
    field="email",
    details={"provided": user_input}
)
```

#### `raise_auth_error(message: str = "Authentication failed") -> None`

Convenience function to raise AuthenticationException.

#### `raise_permission_error(message: str = "Access denied") -> None`

Convenience function to raise AuthorizationException.

---

## Configuration

### Middleware Order

**Critical:** Middleware must be registered in this exact order in `main.py`:

```python
# 1. CORS (if needed)
app.add_middleware(CORSMiddleware, ...)

# 2. Store Context Detection (MUST BE FIRST)
app.middleware("http")(store_context_middleware)

# 3. Context Detection (MUST BE AFTER STORE)
app.middleware("http")(context_middleware)

# 4. Theme Context (MUST BE AFTER CONTEXT)
app.middleware("http")(theme_context_middleware)

# 5. Logging (optional, should be last)
app.add_middleware(LoggingMiddleware)
```

**Why This Order Matters:**
1. Store context must set `request.state.store` first
2. Context detection needs store info to identify SHOP context
3. Theme context needs store info to load theme
4. Logging should be last to capture all middleware activity

### Template Directory

Configure in `main.py`:

```python
from pathlib import Path

BASE_DIR = Path(__file__).resolve().parent
TEMPLATES_DIR = BASE_DIR / "app" / "templates"

templates = Jinja2Templates(directory=str(TEMPLATES_DIR))
```

### Error Code Registry

Update as needed in `app/exceptions/error_renderer.py`:

```python
STATUS_CODE_NAMES = {
    400: "Bad Request",
    401: "Unauthorized",
    403: "Forbidden",
    404: "Not Found",
    422: "Validation Error",
    429: "Too Many Requests",
    500: "Internal Server Error",
    502: "Bad Gateway",
    503: "Service Unavailable",
    # Add new codes here
}

STATUS_CODE_MESSAGES = {
    400: "The request could not be processed due to invalid data.",
    # ... etc
}
```

---

## Best Practices Summary

### DO:
✅ Use specific exception classes for different error scenarios
✅ Provide clear, user-friendly error messages
✅ Include relevant details in the `details` dictionary
✅ Use consistent error codes across the application
✅ Test both API and HTML responses
✅ Keep error templates simple and accessible
✅ Use debug mode responsibly (admin only)
✅ Follow the template inheritance pattern
✅ Document any new exception types
✅ Test error pages manually in browsers

### DON'T:
❌ Expose sensitive information in error messages
❌ Use generic exceptions for domain-specific errors
❌ Return HTML for API endpoints
❌ Skip the Accept header check
❌ Hardcode HTML in exception handlers
❌ Forget to add fallback templates
❌ Show technical details to customers
❌ Use complex JavaScript in error pages
❌ Forget to test 401 redirects
❌ Mix API and page response logic

---

## Support and Questions

For questions or issues with the error handling system:

1. **Check Logs:** Look for `[CONTEXT]` and `[ERROR]` log messages
2. **Review This Documentation:** Most issues are covered in Troubleshooting
3. **Check Implementation Status:** Verify which phase is complete
4. **Test Locally:** Reproduce the issue with curl/browser
5. **Contact:** [Your team contact information]

---

**Document Version:** 1.0.0
**Last Updated:** 2025
**Maintained By:** [Your team name]
**Next Review:** After Phase 2 & 3 completion
