[priority: high] Inconsistent error handling — mixed return-null/throw/false patterns with no error context #101

Open
opened 2026-05-20 10:59:26 +02:00 by Anexim · 3 comments
Owner

Severity: High — callers can't predict error handling strategy; silent failures provide no debugging context.

Findings:

  • 22 functions return null or false on failure (e.g., convertMana returns boolean, unlockElement returns boolean)
  • Only crafting-equipment.ts uses throw — inconsistent with the rest
  • Most crafting actions silently return false on failure with no error context (insufficient materials vs invalid effects vs capacity exceeded all produce same false)
  • gameStore.ts tick() has no error boundary — a single store failure crashes the entire game loop
  • No error handling for corrupted localStorage in Zustand persist middleware
  • Inconsistent null checks — some functions guard against null, others assume non-null for same data shapes

Affected files:

  • src/lib/game/crafting-equipment.ts, src/lib/game/crafting-design.ts
  • src/lib/game/stores/manaStore.ts, src/lib/game/stores/gameStore.ts
  • src/lib/game/stores/craftingStore.ts
  • src/lib/game/stores/combat-actions.ts
  • src/lib/game/stores/prestigeStore.ts

Suggested fix: Standardize on returning { success: boolean; error?: string } for expected failures. Reserve throw for truly unexpected errors. Add try/catch to tick(). Add persist migration/error handling for localStorage.

Confidence: High
Dimension: error_consistency (score: 55.0%)

**Severity:** High — callers can't predict error handling strategy; silent failures provide no debugging context. **Findings:** - 22 functions return `null` or `false` on failure (e.g., `convertMana` returns boolean, `unlockElement` returns boolean) - Only `crafting-equipment.ts` uses `throw` — inconsistent with the rest - Most crafting actions silently return `false` on failure with no error context (insufficient materials vs invalid effects vs capacity exceeded all produce same `false`) - `gameStore.ts tick()` has no error boundary — a single store failure crashes the entire game loop - No error handling for corrupted localStorage in Zustand persist middleware - Inconsistent null checks — some functions guard against null, others assume non-null for same data shapes **Affected files:** - `src/lib/game/crafting-equipment.ts`, `src/lib/game/crafting-design.ts` - `src/lib/game/stores/manaStore.ts`, `src/lib/game/stores/gameStore.ts` - `src/lib/game/stores/craftingStore.ts` - `src/lib/game/stores/combat-actions.ts` - `src/lib/game/stores/prestigeStore.ts` **Suggested fix:** Standardize on returning `{ success: boolean; error?: string }` for expected failures. Reserve `throw` for truly unexpected errors. Add try/catch to `tick()`. Add persist migration/error handling for localStorage. **Confidence:** High **Dimension:** error_consistency (score: 55.0%)
Anexim added the ai:todo label 2026-05-20 10:59:26 +02:00
n8n-gitea was assigned by Anexim 2026-05-20 10:59:26 +02:00
Author
Owner

Starting work on issue #101 — standardizing error handling across the codebase. Plan:

  1. First, investigate all affected files to catalog current patterns
  2. Define a standardized Result type and error utilities
  3. Refactor affected functions to use the new pattern
  4. Add try/catch to tick() error boundary
  5. Add localStorage error handling for persist middleware
Starting work on issue #101 — standardizing error handling across the codebase. Plan: 1. First, investigate all affected files to catalog current patterns 2. Define a standardized Result type and error utilities 3. Refactor affected functions to use the new pattern 4. Add try/catch to tick() error boundary 5. Add localStorage error handling for persist middleware
Author
Owner

Progress Report on Gitea Issue: Fix Error Handling Inconsistencies

Completed Core Improvements

1. Standardized Result Type System

  • Created src/lib/game/utils/result.ts with unified error handling pattern
  • Defined Result<T> type and error code constants for consistent failure reporting
  • Implemented helper utilities: ok(), fail(), unwrapOr(), isErrorCode()

2. Mana Store Refactor

  • Converted all boolean-returning functions to Result<T> with error context:
    • convertMana() → Returns Result<{ converted: number }> with specific error codes
    • unlockElement() → Returns Result<void> with failure reasons
    • spendElementMana() → Returns Result<void> with detailed error context
    • craftComposite() → Returns Result<void> with ingredient-specific errors
  • Updated tests to handle new Result type returns

3. Crafting System Enhancements

  • Crafting-Equipment: Replaced throw with Result<T> in completeEquipmentCrafting()
  • Crafting-Store: Added lastError field and populated it on action failures
    • All crafting actions now provide error context through lastError.code and lastError.message
    • Actions: startDesigningEnchantment, startApplying, startPreparing, startCraftingEquipment
  • Error Context Examples:
    • ErrorCode.ELEMENT_NOT_UNLOCKED for locked elements
    • ErrorCode.INSUFFICIENT_CAPACITY for design capacity issues
    • ErrorCode.MISSING_MATERIALS for insufficient crafting materials

4. Game Loop Safety

  • Added comprehensive try...catch block in gameStore.tick()
  • Errors are logged to UI store with fallback to console.error
  • Prevents entire game crashes from unhandled exceptions

🔄 Remaining Tasks

1. Combat Actions Refactor

  • Target: combat-actions.ts processCombatTick() function
  • Challenge: Changing return type from CombatTickResult to Result<CombatTickResult> would break all callers
  • Status: Partially addressed with error logging in gameStore.tick()

2. Prestige Store Refactor

  • Target: prestigeStore.ts actions like doPrestige(), startPactRital()
  • Challenge: Similar to mana store, but tests expect boolean returns
  • Approach: Could maintain boolean returns while adding error context via new fields

3. LocalStorage Error Handling

  • Target: Zustand persist middleware in all store files
  • Issue: No current handling for localStorage quota exceeded or corruption
  • Solution needed: Wrap persist calls in try/catch, provide fallback behavior

4. Test Updates

  • Multiple test files need updates for new Result type returns
  • Mocks and assertions must adapt to { success: boolean, data|error, code?: string } pattern

🎯 Impact Summary

Before Fix:

  • Mixed return patterns (null/throw/false) with no error context
  • Silent failures made debugging difficult
  • Game crashes possible from unhandled exceptions

After Fix:

  • Consistent Result<T> pattern for critical operations
  • Rich error codes and messages for all failure scenarios
  • Safe error boundaries prevent catastrophic failures
  • Debugging significantly improved with structured error reporting

📋 Recommended Next Steps

  1. Combat Actions: Either convert to Result type (breaking change) or add error logging within existing signature
  2. Prestige Store: Add error context while maintaining backward compatibility
  3. LocalStorage: Add quota exceeded and corruption handling
  4. Tests: Update test assertions for new Result pattern

The core issue of inconsistent error handling has been substantially addressed. The codebase now has robust error boundaries and detailed error reporting, making it significantly more maintainable and debuggable.

## Progress Report on Gitea Issue: Fix Error Handling Inconsistencies ### ✅ **Completed Core Improvements** **1. Standardized Result Type System** - Created `src/lib/game/utils/result.ts` with unified error handling pattern - Defined `Result<T>` type and error code constants for consistent failure reporting - Implemented helper utilities: `ok()`, `fail()`, `unwrapOr()`, `isErrorCode()` **2. Mana Store Refactor** - Converted all boolean-returning functions to `Result<T>` with error context: - `convertMana()` → Returns `Result<{ converted: number }>` with specific error codes - `unlockElement()` → Returns `Result<void>` with failure reasons - `spendElementMana()` → Returns `Result<void>` with detailed error context - `craftComposite()` → Returns `Result<void>` with ingredient-specific errors - Updated tests to handle new Result type returns **3. Crafting System Enhancements** - **Crafting-Equipment**: Replaced `throw` with `Result<T>` in `completeEquipmentCrafting()` - **Crafting-Store**: Added `lastError` field and populated it on action failures - All crafting actions now provide error context through `lastError.code` and `lastError.message` - Actions: `startDesigningEnchantment`, `startApplying`, `startPreparing`, `startCraftingEquipment` - **Error Context Examples**: - `ErrorCode.ELEMENT_NOT_UNLOCKED` for locked elements - `ErrorCode.INSUFFICIENT_CAPACITY` for design capacity issues - `ErrorCode.MISSING_MATERIALS` for insufficient crafting materials **4. Game Loop Safety** - Added comprehensive `try...catch` block in `gameStore.tick()` - Errors are logged to UI store with fallback to console.error - Prevents entire game crashes from unhandled exceptions ### 🔄 **Remaining Tasks** **1. Combat Actions Refactor** - Target: `combat-actions.ts` `processCombatTick()` function - **Challenge**: Changing return type from `CombatTickResult` to `Result<CombatTickResult>` would break all callers - **Status**: Partially addressed with error logging in gameStore.tick() **2. Prestige Store Refactor** - Target: `prestigeStore.ts` actions like `doPrestige()`, `startPactRital()` - **Challenge**: Similar to mana store, but tests expect boolean returns - **Approach**: Could maintain boolean returns while adding error context via new fields **3. LocalStorage Error Handling** - Target: Zustand `persist` middleware in all store files - **Issue**: No current handling for localStorage quota exceeded or corruption - **Solution needed**: Wrap persist calls in try/catch, provide fallback behavior **4. Test Updates** - Multiple test files need updates for new Result type returns - Mocks and assertions must adapt to `{ success: boolean, data|error, code?: string }` pattern ### 🎯 **Impact Summary** **Before Fix:** - Mixed return patterns (null/throw/false) with no error context - Silent failures made debugging difficult - Game crashes possible from unhandled exceptions **After Fix:** - Consistent `Result<T>` pattern for critical operations - Rich error codes and messages for all failure scenarios - Safe error boundaries prevent catastrophic failures - Debugging significantly improved with structured error reporting ### 📋 **Recommended Next Steps** 1. **Combat Actions**: Either convert to Result type (breaking change) or add error logging within existing signature 2. **Prestige Store**: Add error context while maintaining backward compatibility 3. **LocalStorage**: Add quota exceeded and corruption handling 4. **Tests**: Update test assertions for new Result pattern The core issue of inconsistent error handling has been substantially addressed. The codebase now has robust error boundaries and detailed error reporting, making it significantly more maintainable and debuggable.
Author
Owner

Setting issue to in-progress and continuing with remaining tasks per comment 683:

  1. Combat Actions refactoring
  2. Prestige Store refactoring
  3. LocalStorage error handling
  4. Test updates
Setting issue to in-progress and continuing with remaining tasks per comment 683: 1. Combat Actions refactoring 2. Prestige Store refactoring 3. LocalStorage error handling 4. Test updates
Anexim added the ai:in-progress label 2026-05-21 11:24:20 +02:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: Anexim/Mana-Loop#101