---
title: "Error Handling & Safety"
description: "Error handling, WHERE validation, and safety features"
canonical_url: "https://docs.ctrl-neo.dev/en/tauri-sqlite-orm/advanced-topics/error-handling-and-safety"
---
# Error Handling & Safety

> Error handling, WHERE validation, and safety features

The ORM includes several safety features to prevent common database mistakes and provides custom error classes for better error handling.

## Custom Error Classes

The ORM provides specific error classes to help you handle different types of errors appropriately.

### Error Hierarchy

```typescript
import {
  TauriORMError,              // Base error class
  QueryBuilderError,          // Query building errors
  MissingWhereClauseError,    // Missing WHERE in UPDATE/DELETE
  ValidationError,            // General validation errors
  InsertValidationError,      // Insert-specific validation
  UpdateValidationError,      // Update-specific validation
  MigrationError,             // Migration errors
  RelationError,              // Relation errors
  ColumnNotFoundError,        // Column doesn't exist
  TableNotFoundError,         // Table doesn't exist
} from '@type32/tauri-sqlite-orm';
```

### Catching Specific Errors

```ts
try {
  await db.update(users)
    .set({ name: 'Bob' })
    .execute(); // Missing WHERE clause
} catch (error) {
  if (error instanceof MissingWhereClauseError) {
    console.error('You forgot to add a WHERE clause!');
    console.error(error.message);
    // Handle this specific error type
  } else if (error instanceof UpdateValidationError) {
    console.error('Update validation failed');
  } else {
    // Re-throw unknown errors
    throw error;
  }
}
```

## WHERE Clause Validation

**Critical Safety Feature**: UPDATE and DELETE operations require a WHERE clause by default to prevent accidental data loss.

### The Problem

Without validation, it's easy to accidentally update or delete all rows:

```ts
// ❌ DANGEROUS: Would update ALL users if not prevented!
await db.update(users).set({ isActive: false }).execute();

// ❌ DANGEROUS: Would delete ALL posts if not prevented!
await db.delete(posts).execute();
```

### The Solution

The ORM requires WHERE clauses for UPDATE and DELETE:

```ts
// ❌ Throws MissingWhereClauseError
await db.update(users)
  .set({ isActive: false })
  .execute();

// ✅ Works - has WHERE clause
await db.update(users)
  .set({ isActive: false })
  .where(eq(users._.columns.id, 123))
  .execute();

// ✅ Works - explicit global operation
await db.update(users)
  .set({ isActive: false })
  .allowGlobalOperation()  // ← Explicitly allows no WHERE clause
  .execute();
```

### Error Message

```text
MissingWhereClauseError: UPDATE operation on table "users" requires a WHERE clause to prevent accidental data loss. Use .where() to specify conditions, or use .allowGlobalOperation() to explicitly allow operations without WHERE.
```

### Intentional Global Operations

When you genuinely need to update/delete all rows, use `.allowGlobalOperation()`:

```ts
// Reset all daily counts - documented and intentional
await db.update(userStats)
  .set({ dailyCount: 0 })
  .allowGlobalOperation()
  .execute();

// Clear temporary cache
await db.delete(tempCache)
  .allowGlobalOperation()
  .execute();
```

## Validation Errors

### Insert Validation

```ts
try {
  // ❌ No data provided
  await db.insert(users).execute();
} catch (error) {
  if (error instanceof InsertValidationError) {
    console.error(error.message);
    // "No data provided for insert. Use .values() to provide data."
  }
}
```

### Update Validation

```ts
try {
  // ❌ No SET clause
  await db.update(users)
    .where(eq(users._.columns.id, 1))
    .execute();
} catch (error) {
  if (error instanceof UpdateValidationError) {
    console.error(error.message);
    // "Cannot execute an update query without a .set(), .increment(), or .decrement() call."
  }
}
```

### Column Not Found

```ts
try {
  await db.update(users)
    .increment('nonExistentColumn' as any)
    .where(eq(users._.columns.id, 1))
    .execute();
} catch (error) {
  if (error instanceof ColumnNotFoundError) {
    console.error(error.message);
    // "Column \"nonExistentColumn\" does not exist on table \"users\""
  }
}
```

## Best Practices

### 1. Always Use WHERE Clauses

```ts
// ❌ BAD: Missing WHERE
await db.delete(users).execute();

// ✅ GOOD: Has WHERE
await db.delete(users)
  .where(eq(users._.columns.id, userId))
  .execute();
```

### 2. Catch Specific Errors

```ts
async function updateUser(userId: number, data: any) {
  try {
    return await db.update(users)
      .set(data)
      .where(eq(users._.columns.id, userId))
      .execute();
  } catch (error) {
    if (error instanceof MissingWhereClauseError) {
      // Log and handle missing WHERE
      logger.error('Missing WHERE clause in update');
      throw new Error('Invalid update operation');
    } else if (error instanceof ColumnNotFoundError) {
      // Handle invalid column
      logger.error('Invalid column in update:', error.message);
      throw new Error('Invalid field name');
    } else {
      // Re-throw database errors
      throw error;
    }
  }
}
```

### 3. Validate Before Operations

```ts
async function safeDeleteUser(userId: number) {
  // Check if user exists first
  const exists = await db.select(users)
    .where(eq(users._.columns.id, userId))
    .exists();
  
  if (!exists) {
    throw new ValidationError('User not found');
  }
  
  // Delete with proper WHERE clause
  return await db.delete(users)
    .where(eq(users._.columns.id, userId))
    .execute();
}
```

### 4. Document Global Operations

```ts
/**
 * Resets all user statistics for a new day
 * This intentionally updates ALL rows
 */
async function resetDailyStats() {
  await db.update(userStats)
    .set({ 
      dailyViews: 0,
      dailyLikes: 0 
    })
    .allowGlobalOperation()  // Documented intent
    .execute();
}
```

### 5. Use Type-Safe Operations

```ts
// TypeScript will catch column name errors at compile time
await db.update(users)
  .increment('views')  // ✓ Type-checked column name
  .where(eq(users._.columns.id, 1))
  .execute();

// ❌ TypeScript error if column doesn't exist
await db.update(users)
  .increment('invalidColumn')  // Type error!
  .execute();
```

## Error Handling Pattern

Here's a comprehensive error handling pattern for production code:

```ts
import {
  MissingWhereClauseError,
  ColumnNotFoundError,
  ValidationError,
  TauriORMError
} from '@type32/tauri-sqlite-orm';

async function updateUserProfile(userId: number, updates: Partial<User>) {
  try {
    // Validate user exists
    const exists = await db.select(users)
      .where(eq(users._.columns.id, userId))
      .exists();
    
    if (!exists) {
      throw new ValidationError('User not found');
    }
    
    // Perform update
    const result = await db.update(users)
      .set({
        ...updates,
        updatedAt: new Date()
      })
      .where(eq(users._.columns.id, userId))
      .returningFirst();
    
    return result;
    
  } catch (error) {
    // Handle ORM-specific errors
    if (error instanceof TauriORMError) {
      logger.error('Database error:', {
        type: error.name,
        message: error.message,
        userId
      });
      
      // Provide user-friendly message
      if (error instanceof MissingWhereClauseError) {
        throw new Error('Invalid update operation');
      } else if (error instanceof ColumnNotFoundError) {
        throw new Error('Invalid field name');
      } else if (error instanceof ValidationError) {
        throw error; // Re-throw validation errors as-is
      }
    }
    
    // Handle other errors
    logger.error('Unexpected error:', error);
    throw new Error('Failed to update user');
  }
}
```

## Summary

The ORM provides multiple layers of safety:

1. **Custom Error Classes** - Clear, specific error messages
2. **WHERE Validation** - Prevents accidental mass updates/deletes
3. **Type Safety** - TypeScript catches errors at compile time
4. **Explicit Global Operations** - `.allowGlobalOperation()` makes intent clear
5. **Validation Errors** - Helpful messages for common mistakes

These features work together to make your database operations safer and more reliable in production applications.


## Sitemap

See the full [sitemap](https://docs.ctrl-neo.dev/sitemap.md) for all pages.
