You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
motief/.mindmodel/constraints/error-handling.md

3.8 KiB

title category severity
Error Handling Patterns constraints high

Error Handling Patterns

Core Rules

  1. Catch Exception, return safe fallbacks (False/[]/None)
  2. Log exceptions with traceback using _logger.exception()
  3. Never swallow exceptions silently - always log or return sensible default
  4. Avoid nested try/except blocks - flatten exception handling

Pattern: Try/Except Safe Fallback

This is the dominant pattern in the codebase (219+ instances).

# Standard pattern from database.py, api_client.py, etc.
try:
    result = risky_operation()
    return process(result)
except Exception as exc:
    _logger.warning("Operation failed: %s", exc)
    return safe_fallback  # False, [], None, {}

Examples from Codebase

database.py - DuckDB operations:

def get_svd_vectors(self, window: str):
    try:
        conn = duckdb.connect(self.db_path, read_only=True)
        try:
            result = conn.execute(query, (window,)).fetchall()
            return self._parse_vectors(result)
        finally:
            conn.close()
    except Exception as exc:
        _logger.warning("Failed to get SVD vectors: %s", exc)
        return []

ai_provider.py - HTTP retries:

try:
    resp = requests.post(url, json=json, headers=headers, timeout=10)
    resp.raise_for_status()
    return resp.json()
except requests.ConnectionError as exc:
    if attempt == retries:
        raise ProviderError(f"Connection error: {exc}") from exc
    # ... retry logic

Pattern: Optional Dependency Fallback

Gracefully degrade when optional packages are unavailable.

# UMAP fallback in explorer_helpers.py
try:
    import umap
    HAS_UMAP = True
except ImportError:
    HAS_UMAP = False
    _logger.debug("UMAP not available, using SVD vectors directly")

def project_to_2d(vectors):
    if HAS_UMAP:
        return umap.UMAP().fit_transform(vectors)
    return vectors[:, :2]  # Fallback: first 2 SVD dimensions

Anti-Patterns

1. Bare except with pass (CRITICAL)

File: database.py, line 47

# BAD - catches KeyboardInterrupt, SystemExit, MemoryError
try:
    conn.execute("CREATE SEQUENCE IF NOT EXISTS motions_id_seq START 1")
except:  # bare except
    pass

Fix: Catch specific exception or log and continue:

try:
    conn.execute("CREATE SEQUENCE IF NOT EXISTS motions_id_seq START 1")
except Exception as exc:
    _logger.debug("Sequence creation skipped (may already exist): %s", exc)

2. Nested Exception Handling

File: explorer.py, lines 244-261

# BAD - opaque error paths
try:
    result = compute_svd(motions)
except Exception:
    try:
        result = fallback_compute(motions)
    except Exception:
        pass  # Both exceptions silently dropped

Fix: Flatten and handle each case explicitly:

# GOOD - explicit handling
try:
    result = compute_svd(motions)
except Exception as exc:
    _logger.warning("SVD failed, trying fallback: %s", exc)
    try:
        result = fallback_compute(motions)
    except Exception as fallback_exc:
        _logger.error("Both SVD approaches failed: %s, %s", exc, fallback_exc)
        raise

Rule Summary

Pattern When to Use Return Value
Safe fallback Best-effort operations [], {}, False, None
Re-raise Critical operations that must succeed raise
Log and continue Optional steps in pipeline (continue)
Graceful degradation Optional dependencies Default behavior

When to Log vs Return

Scenario Action
User action fails Log warning, return safe default
Internal error (corrupt data) Log error, return safe default
Transient failure (network) Log warning, retry if appropriate
Configuration error Log error, raise with clear message