# Exception Handling Audit Report (Data-Oriented Convention Compliance) **Date:** 2026-06-16 **Track ID:** `exception_handling_audit_20260616` **Status:** COMPLETED (5/5 phases) **Reviewer:** User (handoff for next-track decision) --- ## 0. TL;DR A static analyzer (`scripts/audit_exception_handling.py`) classified every `try/except/finally/raise` site in the codebase (65 files, 348 sites) against the data-oriented error handling convention established by `data_oriented_error_handling_20260606` (shipped 2026-06-12). | Headline | Count | |---|---| | Total sites | 348 | | Compliant sites | 80 (23%) | | Suspicious sites | 25 (7%) | | Violation sites | 211 (61%) | | Unclear (manual review) | 32 (9%) | **Key finding:** the convention is **partially applied** (3 of 65 src/ files are refactored: `mcp_client.py`, `ai_client.py`, `rag_engine.py`). The remaining ~10 files in `src/` are in the **migration-target state**. | File Group | Sites | Violations | Note | |---|---|---|---| | **Baseline (3 refactored files)** | 112 | 77 | Convention reference; even these have remaining `except Exception + log` patterns that should be Result-converted | | **Migration target (62 other files)** | 236 | 134 | The work for future refactor tracks | **What the user decides:** which migration-target file is the next refactor track? The top 5 candidates by violation count are: `gui_2.py` (37), `app_controller.py` (35), `session_logger.py` (8), `warmup.py` (6), `theme_models.py` (6). **Important:** the "violation count" is **NOT a bug count**. These are migration-target sites, not bugs. The codebase works correctly today (1288 + 4 + 0 test pass). The audit identifies which files would benefit from future refactor tracks; the user decides what to migrate. --- ## 1. Methodology ### 1.1 The 10 Classification Categories The audit classifies each site into one of 10 categories (5 compliant, 3 violation, 1 suspicious, 1 unclear): | Category | Convention status | When | |---|---|---| | `BOUNDARY_SDK` | Compliant | Wraps a third-party SDK call | | `BOUNDARY_IO` | Compliant | Wraps stdlib I/O that can raise | | `BOUNDARY_CONVERSION` | Compliant | Catches and converts to `ErrorInfo` in a `Result` | | `BOUNDARY_FASTAPI` | Compliant | FastAPI `HTTPException` in `_api_*` handler | | `INTERNAL_SILENT_SWALLOW` | **Violation** | `except ...: pass` or just logs | | `INTERNAL_BROAD_CATCH` | **Violation** | `except Exception` without ErrorInfo conversion, in non-`*_result` code | | `INTERNAL_OPTIONAL_RETURN` | **Violation** | `try/except + return None/Optional[T]` | | `INTERNAL_RETHROW` | Suspicious | `try/except + raise` (without ErrorInfo conversion) | | `INTERNAL_PROGRAMMER_RAISE` | Compliant | `raise` for impossible state / precondition (`__init__`, `assert`, `ValueError`) | | `INTERNAL_COMPLIANT` | Compliant | `try/finally` (no except) — canonical cleanup | | `UNCLEAR` | Review needed | Can't determine automatically | ### 1.2 The Baseline vs Migration-Target Split The 3 fully-refactored files (per the `data_oriented_error_handling_20260606` track) are the **baseline** — the convention reference. The other ~62 files are the **migration target**. The audit reports both separately so the user can distinguish "the convention has gaps even in the refactored files" from "the convention has not been applied to the unrefactored files". ### 1.3 The Script's Classification Logic The script uses Python's `ast` module (not regex) to walk each source file's AST and classify each `try/except/finally/raise` node. The classification considers: 1. **The exception type** (third-party SDK exception, stdlib I/O exception, FastAPI exception, programmer-error exception, etc.) 2. **The enclosing function name** (`_api_*` for FastAPI, `*_result` for Result-returning, `__init__` for constructors) 3. **The return type annotation** of the enclosing function (`Result[T]` vs `Optional[T]` vs plain `T`) 4. **What the catch site does with the exception** (ErrorInfo conversion, re-raise, return None, silent swallow, etc.) 5. **What the try body calls** (third-party SDK module vs internal method) The script outputs a 1-line hint per site suggesting what the fix could look like (e.g., "return `Result(data=NIL_T, errors=[...])`"). ### 1.4 What the Script Does NOT Do - Does NOT execute the code (it's a static analyzer; no behavior change). - Does NOT modify any files. - Does NOT provide specific refactor patches (the "hint" is a 1-line suggestion; the implementer of the next refactor track writes the actual code). - Does NOT verify that refactored code works (no test execution; the audit report is the deliverable). --- ## 2. The 3 Refactored Baseline Files (Convention Reference) These 3 files are the convention reference. Sites in these files are labeled `in_refactored_baseline: true` in the JSON output. ### 2.1 `src/mcp_client.py` (refactored 2026-06-12) - **Total sites:** 53 - **Violations:** 44 (40 `INTERNAL_BROAD_CATCH` + 4 `INTERNAL_SILENT_SWALLOW`) - **Compliant sites:** 5 - **Unclear:** 4 **Note:** the spec for the parent track chose "Path C" (additive `*_result` variants alongside the existing `(p, err)` tuple API). The 30+ tool-function refactor + assertion chain removal is deferred. The 44 violations are mostly the remaining `(p, err)` + `except Exception + log` patterns in the 30+ tool functions that haven't been refactored yet. ### 2.2 `src/ai_client.py` (refactored 2026-06-12) - **Total sites:** 46 - **Violations:** 27 (18 `INTERNAL_BROAD_CATCH` + 9 `INTERNAL_SILENT_SWALLOW`) - **Compliant sites:** 8 - **Suspicious sites:** 9 - **Unclear:** 2 **Note:** the `ProviderError` exception class was REMOVED; all 8 `_send__result()` functions return `Result[str]`. The 27 violations are mostly the broad-catches in the SDK-exception-classification helpers (which catch `anthropic.APIError`, `google.api_core.exceptions.*`, etc., but don't convert to ErrorInfo at the catch site — they log and re-raise). ### 2.3 `src/rag_engine.py` (refactored 2026-06-12) - **Total sites:** 13 - **Violations:** 6 (5 `INTERNAL_BROAD_CATCH` + 1 `INTERNAL_SILENT_SWALLOW`) - **Compliant sites:** 1 - **Suspicious sites:** 8 **Note:** `_init_vector_store_result` and `_validate_collection_dim_result` return `Result[None]` with ErrorInfo conversion. The 6 violations are the remaining broad-catches in non-`*_result` methods (`add_documents`, etc.). ### 2.4 The 77 Baseline Violations Are NOT Bugs The 77 violations in the 3 refactored files are **migration-target sites in files that are otherwise convention-compliant**. The refactor was incomplete (per the parent's Path C decision for mcp_client and the incremental migration strategy). The user can decide to do follow-up refactors to close these 77 sites, or to accept them as "good enough for the convention reference" and focus on the larger unrefactored files. --- ## 3. Per-File Violation Counts (Top 15 Migration-Target Files) | Rank | File | Total | Violations | Suspicious | Unclear | Compliant | Note | |---|---|---|---|---|---|---|---| | 1 | `src/gui_2.py` (260KB) | 54 | 37 | 2 | 13 | 2 | Largest file; 25 `INTERNAL_BROAD_CATCH` + 12 `INTERNAL_SILENT_SWALLOW` | | 2 | `src/app_controller.py` (166KB) | 56 | 35 | 3 | 2 | 16 | 13 of 35 are FastAPI boundary (compliant); 22 are migration-target | | 3 | `src/session_logger.py` | 8 | 8 | 0 | 0 | 0 | All silent-swallow + broad-catch | | 4 | `src/warmup.py` | 7 | 6 | 1 | 0 | 0 | Startup-time broad-catches | | 5 | `src/theme_models.py` | 10 | 6 | 0 | 2 | 2 | Mostly re-raise | | 6 | `src/api_hooks.py` | 5 | 5 | 0 | 0 | 0 | FastAPI HookServer; many broad-catches | | 7 | `src/project_manager.py` | 5 | 5 | 0 | 0 | 0 | 3 silent-swallow + 2 broad-catch | | 8-15 | (10 other files, 0-3 violations each) | 91 | 32 | 6 | 14 | 39 | Mixed; small files | **Total migration-target sites:** 236 **Total migration-target violations:** 134 **Total migration-target compliant:** 70 (mostly `INTERNAL_PROGRAMMER_RAISE` in `__init__` + `try/finally` cleanup patterns + a few `BOUNDARY_SDK` from chromadb/requests imports) --- ## 4. Per-Category Breakdown ### 4.1 Violations (211 sites, 61% of total) | Category | Count | Typical pattern | Fix hint | |---|---|---|---| | `INTERNAL_BROAD_CATCH` | 147 | `try: ...; except Exception: log(...)` | Narrow the exception type OR convert to `ErrorInfo` in a `Result` | | `INTERNAL_SILENT_SWALLOW` | 61 | `try: ...; except SomeError: pass` | Let it propagate OR `return Result(data=NIL_T, errors=[...])` OR document with `assert` | | `INTERNAL_OPTIONAL_RETURN` | 3 | `try: ...; except: return None` | Replace with `Result[T]` returning `Result(data=NIL_T, errors=[...])` | ### 4.2 Compliant (80 sites, 23% of total) | Category | Count | Typical pattern | |---|---|---| | `INTERNAL_PROGRAMMER_RAISE` | 25 | `raise ValueError` in `__init__`; `assert` for impossible states | | `BOUNDARY_SDK` | 19 | `except anthropic.APIError`; `except google.api_core.exceptions.*` | | `INTERNAL_COMPLIANT` | 16 | `try/finally` cleanup pattern | | `BOUNDARY_FASTAPI` | 12 | `raise HTTPException` in `_api_*` handler | | `BOUNDARY_CONVERSION` | 8 | `except Exception as e: return Result(data=..., errors=[ErrorInfo(...)])` | ### 4.3 Suspicious (25 sites, 7% of total) | Category | Count | Typical pattern | Fix hint | |---|---|---|---| | `INTERNAL_RETHROW` | 25 | `try: ...; except: log(); raise` (no conversion) | See "Re-Raise Patterns" in the styleguide; 3 legitimate patterns + 1 suspicious | ### 4.4 Unclear (32 sites, 9% of total) | Category | Count | Typical pattern | |---|---|---| | `UNCLEAR` | 32 | Can't determine automatically; needs human review | The 32 `UNCLEAR` sites are mostly in `src/gui_2.py` (13) and the smaller files (theme_models, project_manager, etc.). They have ambiguous exception-handling patterns where the script's heuristics don't definitively classify. The `--verbose` flag shows each one inline. --- ## 5. The 5 Doc Gaps Closed (this track's secondary deliverable) The audit revealed 5 gaps in the existing documentation of the convention. This track closed all 5. ### 5.1 G1: FastAPI `HTTPException` in `_api_*` handlers (CLOSED) **Gap:** the styleguide said "exceptions are reserved for the SDK boundary" but didn't address the FastAPI framework boundary. The audit found 13 sites in `src/app_controller.py` that use FastAPI's idiomatic `HTTPException` pattern. **Fix:** added a new "Boundary Types" section to the styleguide with 3 categories of legitimate boundaries (third-party SDK, stdlib I/O, framework). The framework category explicitly covers FastAPI. The new `docs/guide_app_controller.md` "Exception Handling" section explains the 13 sites in detail. ### 5.2 G2: The "broad except Exception" rule (CLOSED) **Gap:** the styleguide's anti-pattern #6 says "DON'T catch `except Exception` and silently swallow." But `except Exception + ErrorInfo conversion` is the canonical SDK boundary pattern (per the parent's spec §3.3). The rule was ambiguous. **Fix:** added a new "The Broad-Except Distinction" section to the styleguide. The section provides a decision table showing when `except Exception` is compliant (conversion to ErrorInfo) vs when it's a violation (swallow / log-only). The new `BOUNDARY_CONVERSION` and `INTERNAL_BROAD_CATCH` categories in the audit implement this rule. ### 5.3 G3: The "constructors can raise" rule (CLOSED) **Gap:** the styleguide §"When to Use This Convention" mentions "Constructors (`__init__`) that fail with programmer errors (use `assert` or `raise` for these)" but the wording is brief. The audit found multiple legitimate `ValueError` raises in `__init__` and `assert` sites. **Fix:** added a new "Constructors Can Raise" section to the styleguide with 2 code examples (the `ValueError` pattern + the `assert` pattern) and a list of 9 recognized programmer-error exception types. The new `INTERNAL_PROGRAMMER_RAISE` category in the audit implements this rule. ### 5.4 G4: The "re-raise" pattern (CLOSED) **Gap:** the styleguide's anti-patterns say "DON'T raise a custom exception class for runtime failures" but re-raising is a separate concern that needs its own rule. The audit found 25 `try/except + raise` sites in `src/`. **Fix:** added a new "Re-Raise Patterns" section to the styleguide with 3 legitimate re-raise patterns (convert, log, cleanup) + 1 suspicious pattern (catch + re-raise the same exception). The new `INTERNAL_RETHROW` category in the audit implements this rule. ### 5.5 G5: The audit script reference (CLOSED) **Gap:** the new `scripts/audit_exception_handling.py` wasn't referenced from any of the convention's documentation. **Fix:** added a new "Audit Script" section to the styleguide. The section documents the script's usage, the classification categories, the "delete to turn off" pattern (per `feature_flags.md`), and the output structure. Also added a cross-reference from `conductor/product-guidelines.md` "Data-Oriented Error Handling" section. --- ## 6. The Migration Target (the work for future refactor tracks) The 211 violations are distributed across 42 files. The user decides which file(s) to migrate next. The top 3 candidates by violation count: ### 6.1 `src/gui_2.py` (37 violations, 260KB) The largest file in the codebase. The 37 violations are mostly the `INTERNAL_BROAD_CATCH` (25) + `INTERNAL_SILENT_SWALLOW` (12) patterns. 13 sites are `UNCLEAR` (manual review needed). **Migration scope estimate:** 2-3 days Tier 2 work to migrate the file to the convention. The work would be: convert `Optional[T]` return types to `Result[T]`; convert `except Exception + log/print` to `except Exception + return Result(...)`; add tests for the new Result-based API. **Risk:** the file is the GUI rendering layer; changes here affect every render frame. The migration should be done incrementally with the hot-reload mechanism (`Ctrl+Alt+R`) so the user can verify each change visually. ### 6.2 `src/app_controller.py` (35 violations + 16 compliant, 166KB) The headless orchestrator. The 35 violations are 28 `INTERNAL_BROAD_CATCH` + 6 `INTERNAL_SILENT_SWALLOW` + 1 `INTERNAL_OPTIONAL_RETURN`. The 16 compliant sites are 13 FastAPI boundary + 3 `INTERNAL_PROGRAMMER_RAISE`. **Migration scope estimate:** 2-3 days Tier 2 work. The 13 FastAPI boundary sites stay as-is (they're the framework contract). The 22 migration-target sites are the work. **Risk:** the controller is the orchestrator and touches every subsystem. Changes here require careful coordination with the `_predefined_callbacks` and `_gettable_fields` registries (per the Hook API). The migration should be done in 5-file commits (the parent track's pattern). ### 6.3 `src/session_logger.py` (8 violations) A small file (16KB). The 8 violations are 4 `INTERNAL_BROAD_CATCH` + 4 `INTERNAL_SILENT_SWALLOW`. **Migration scope estimate:** 0.5 day Tier 2 work. The file is small and the migration is straightforward. **Risk:** low. The file is self-contained. --- ## 7. Followup Recommendations (for the user's next-track decision) The user has 4 options for what to do next: | # | Option | Scope | Estimated effort | Rationale | |---|---|---|---|---| | 1 | **Do the planned `send_result` → `send` mass rename** (manual refactor) | Mechanical find-replace of the function name | 1-2 hours | User's stated intent. Mechanical, low-risk. Doesn't change test pass count. | | 2 | **Migrate `app_controller.py` to the convention** | 22 migration-target sites | 2-3 days Tier 2 | The highest-priority migration per the doeh spec §12.2. The 13 FastAPI boundary sites stay. | | 3 | **Migrate `gui_2.py` to the convention** | 37 migration-target sites | 2-3 days Tier 2 | The largest file; would close the biggest single chunk. | | 4 | **Migrate `session_logger.py` + `warmup.py` + `theme_models.py` together** | 20 migration-target sites in 3 small files | 0.5-1 day Tier 2 | Quick wins; clears 3 files at once. | The recommended order is **1 → 2 → 3 → 4** (do the mechanical rename first, then the orchestrator migration, then the GUI, then the small files). The user decides. --- ## 8. Verification Artifacts - `tests/artifacts/exception_handling_audit_final.log` — the human-readable audit output (103 lines, 7.4KB) - `tests/artifacts/exception_handling_audit_final.json` — the JSON output (43.7KB, machine-readable) - `scripts/audit_exception_handling.py` — the static analyzer (792 lines) - `conductor/code_styleguides/error_handling.md` — updated with 5 new sections - `docs/guide_app_controller.md` — updated with the FastAPI boundary section - `conductor/product-guidelines.md` — updated with the audit script cross-reference - `docs/reports/EXCEPTION_HANDLING_AUDIT_20260616.md` — this report --- ## 9. Test Pass Count (unchanged from `rag_test_failures_20260615`) This track is informational (no code change). The test pass count is **1288 + 4 + 0** (unchanged from the previous track's baseline).