# How to Handle Errors in Kaneo: Backend, Frontend and UI Best Practices

> Learn to handle errors in Kaneo with a layered architecture. Discover backend, frontend, and UI best practices for robust error management and user guidance.

- Repository: [kaneo.app/kaneo](https://github.com/usekaneo/kaneo)
- Tags: best-practices
- Published: 2026-08-05

---

**Kaneo handles errors through a layered architecture where the backend throws `HTTPException` status codes, the frontend parses errors into a unified `ApiError` type, and UI components render contextual troubleshooting guidance.**

Kaneo's error handling is implemented across two monorepo packages: the Hono-based API in `apps/api` and the React-based web client in `apps/web`. The system separates backend signaling from frontend presentation, ensuring users receive actionable feedback without exposing internal implementation details.

## Backend Error Handling with HTTPException

The Kaneo API uses **Hono's `HTTPException`** as the primary error signaling mechanism. Controllers throw exceptions with explicit HTTP status codes and optional messages, giving the frontend clear context for categorization.

### Throwing HTTPException in Controllers

In [`apps/api/src/task/controllers/update-task.ts`](https://github.com/usekaneo/kaneo/blob/main/apps/api/src/task/controllers/update-task.ts), the update task controller demonstrates multiple error conditions:

```typescript
import { HTTPException } from 'hono/http-exception';
import { getTaskById } from '@/services/task';

export async function updateTask(c) {
  const { id } = c.req.valid('param');
  const data = await c.req.json();

  const task = await getTaskById(id);
  if (!task) {
    throw new HTTPException(404, { message: 'Task not found' });
  }

  if (!c.get('userId')) {
    throw new HTTPException(401, { message: 'Unauthorized' });
  }

  // …perform update…
  return c.json({ success: true });
}

```

Common `HTTPException` patterns in Kaneo include:

- **400 Bad Request** – Validation failures (e.g., [`apps/api/src/utils/validate-dates.ts`](https://github.com/usekaneo/kaneo/blob/main/apps/api/src/utils/validate-dates.ts))
- **401 Unauthorized** – Missing or expired authentication
- **403 Forbidden** – Permission violations
- **404 Not Found** – Missing resources
- **500 Internal Server Error** – Unexpected server failures

The authentication middleware in [`apps/api/src/utils/authenticate-api-request.ts`](https://github.com/usekaneo/kaneo/blob/main/apps/api/src/utils/authenticate-api-request.ts) similarly converts auth failures into `HTTPException` responses, ensuring consistent error signaling across all API endpoints.

## Frontend Error Parsing with error-handler.ts

The web client centralizes error processing in **[`apps/web/src/lib/error-handler.ts`](https://github.com/usekaneo/kaneo/blob/main/apps/web/src/lib/error-handler.ts)**. This module transforms raw fetch errors into a predictable `ApiError` shape with five classification types.

### The ApiError Type and parseApiError Function

The `parseApiError()` function analyzes caught errors and returns a structured object:

```typescript
// Type definition inferred from implementation
type ApiErrorType = 'network' | 'cors' | 'auth' | 'server' | 'unknown';

interface ApiError {
  type: ApiErrorType;
  message: string;
  original?: Error; // Preserved for debugging
}

```

The parser classifies errors by examining:

- **Network errors** – `navigator.onLine` checks, TypeError from failed connections
- **CORS errors** – Specific error message patterns from fetch failures
- **Auth errors** – 401/403 status codes with auth-related context
- **Server errors** – 5xx status codes from the API
- **Unknown errors** – Fallback for unclassified exceptions

### Troubleshooting Helper Functions

The error handler exports two functions for generating user-facing guidance:

- **`getCorsTroubleshootingSteps()`** – Returns array of CORS configuration checks
- **`getNetworkTroubleshootingSteps()`** – Returns connectivity debugging steps

These enable UI components to display contextual help without hardcoding diagnostic content.

## Integrating Error Handling in Data Fetching

### Basic Fetcher with Error Handling

```typescript
import { parseApiError, getCorsTroubleshootingSteps, getNetworkTroubleshootingSteps } from '@/lib/error-handler';
import { toast } from '@/lib/toast';

export async function fetchTasks(projectId: string) {
  try {
    const resp = await fetch(`${import.meta.env.VITE_API_URL}/projects/${projectId}/tasks`);
    if (!resp.ok) {
      // Wrap non-2xx responses so parseApiError can categorize them
      throw new Error(`HTTP ${resp.status}`);
    }
    return await resp.json();
  } catch (rawErr) {
    const err = parseApiError(rawErr);
    
    toast.error(err.message, {
      // Conditionally show troubleshooting steps based on error type
      details: err.type === 'cors' 
        ? getCorsTroubleshootingSteps()
        : err.type === 'network' 
          ? getNetworkTroubleshootingSteps()
          : [],
    });
    
    // Re-throw for higher-level handling if needed
    throw err;
  }
}

```

### TanStack Query Integration

```typescript
import { useQuery } from '@tanstack/react-query';
import { fetchTasks } from '@/fetchers/task';

export function useProjectTasks(projectId: string) {
  return useQuery(
    ['tasks', projectId], 
    () => fetchTasks(projectId), 
    {
      onError: (err) => {
        // err is already typed as ApiError via parseApiError
        console.error('Task fetch failed', err);
      },
      retry: false, // Let UI handle retry after showing help
    }
  );
}

```

The `retry: false` configuration prevents automatic retries that would mask intermittent errors, allowing the UI to present troubleshooting steps first.

## UI Presentation and Toast Notifications

The **[`apps/web/src/lib/toast.ts`](https://github.com/usekaneo/kaneo/blob/main/apps/web/src/lib/toast.ts)** utility integrates with the error handler to display contextual messages. When `ApiError.type` indicates CORS or network problems, the toast renders expandable help sections using the troubleshooting arrays.

Presentation patterns include:

- **Network errors** – "Unable to connect to the server. Check your internet connection."
- **CORS errors** – "Configuration problem detected. [View troubleshooting steps]"
- **Auth errors** – Prompt re-authentication with direct login link
- **Server errors** – Generic message with error ID for support reference

## Error Handling Architecture Principles

Kaneo's error handling follows four core design principles visible in the source code:

1. **Consistent error shape** – All client code works with `ApiError`, eliminating type guards and defensive coding
2. **Backend-driven status codes** – HTTP status codes from `HTTPException` directly map to `ApiError.type` classifications
3. **Granular troubleshooting** – Helper functions return actionable step arrays, not just static strings
4. **Separation of concerns** – Backend never leaks stack traces; frontend never inspects server internals

## Summary

- Kaneo uses **`HTTPException`** from `hono/http-exception` for backend error signaling with explicit status codes
- The **`parseApiError()`** function in [`apps/web/src/lib/error-handler.ts`](https://github.com/usekaneo/kaneo/blob/main/apps/web/src/lib/error-handler.ts) normalizes all errors into five classified types
- **Troubleshooting helpers** generate contextual guidance for CORS and network failures
- **TanStack Query integration** leverages the error classification for controlled retry behavior and UI feedback
- Full implementation spans [`apps/api/src/task/controllers/update-task.ts`](https://github.com/usekaneo/kaneo/blob/main/apps/api/src/task/controllers/update-task.ts) (backend) and [`apps/web/src/lib/error-handler.ts`](https://github.com/usekaneo/kaneo/blob/main/apps/web/src/lib/error-handler.ts) (frontend)

## Frequently Asked Questions

### How does Kaneo distinguish between CORS and network errors?

The `parseApiError()` function in [`apps/web/src/lib/error-handler.ts`](https://github.com/usekaneo/kaneo/blob/main/apps/web/src/lib/error-handler.ts) examines error message patterns and browser signals. CORS errors produce specific fetch failure messages, while network errors trigger different exception types or `navigator.onLine` state changes. Both map to distinct `ApiError.type` values with corresponding troubleshooting arrays.

### What HTTP status codes should backend controllers throw for common errors?

Per Kaneo's implementation in [`apps/api/src/task/controllers/update-task.ts`](https://github.com/usekaneo/kaneo/blob/main/apps/api/src/task/controllers/update-task.ts) and related files: use **400** for validation failures, **401** for authentication problems, **403** for authorization violations, **404** for missing resources, and **500** for unexpected server errors. Each should include a clear message for user display.

### How can I add retry logic that respects Kaneo's error classification?

Check `err.type` before retrying. Skip retries for **auth** errors (requires user action), **cors** errors (configuration fix needed), and often **unknown** errors. Consider limited retries only for **network** and transient **server** errors. The error handler preserves the original error object in `err.original` for advanced conditional logic.

### Where is the toast UI component configured to show troubleshooting steps?

The toast utility in [`apps/web/src/lib/toast.ts`](https://github.com/usekaneo/kaneo/blob/main/apps/web/src/lib/toast.ts) accepts a `details` array option populated from `getCorsTroubleshootingSteps()` or `getNetworkTroubleshootingSteps()` based on the `ApiError.type`. This pattern appears in fetchers throughout `apps/web/src/fetchers/` when calling `toast.error()`.