generated from john/python-template
120 lines
4.8 KiB
Markdown
120 lines
4.8 KiB
Markdown
---
|
|
description: Cross-cutting error handling rules for services, API, and UI.
|
|
applyTo: 'src/transcription/**/*.py'
|
|
---
|
|
|
|
# Error Handling (Cross-cutting)
|
|
|
|
Primary references:
|
|
|
|
- `docs/error_handling.md`
|
|
- `docs/invariant/error_handling.md`
|
|
- `docs/requirements.md`
|
|
|
|
## Taxonomy and Categories
|
|
|
|
Use category-driven semantics aligned to canonical policy:
|
|
|
|
- `validation`
|
|
- `not_found`
|
|
- `conflict`
|
|
- `external`
|
|
- `timeout`
|
|
- `internal`
|
|
|
|
Do not invent ad hoc categories in user/API-facing envelopes unless canonical docs are updated.
|
|
|
|
Runtime/internal categories may be more specific for diagnostics and persistence, but they must map
|
|
deterministically to the canonical envelope categories through the centralized mapper in
|
|
`transcription.errors.canonical_error_category`.
|
|
|
|
Current internal categories:
|
|
|
|
- `validation_error`
|
|
- `user_input_error`
|
|
- `not_found_error`
|
|
- `conflict_error`
|
|
- `external_provider_error`
|
|
- `external_timeout_error`
|
|
- `processing_error`
|
|
- `infrastructure_transient_error`
|
|
- `infrastructure_persistent_error`
|
|
- `internal_unexpected_error`
|
|
|
|
Required internal -> canonical mapping:
|
|
|
|
- `validation_error`, `user_input_error` -> `validation`
|
|
- `not_found_error` -> `not_found`
|
|
- `conflict_error` -> `conflict`
|
|
- `external_provider_error` -> `external`
|
|
- `external_timeout_error`, `infrastructure_transient_error` -> `timeout`
|
|
- `processing_error`, `infrastructure_persistent_error`, `internal_unexpected_error` -> `internal`
|
|
|
|
## Translation Boundaries
|
|
|
|
- **Provider/adapters:** raise provider/domain exceptions; do not emit UI text.
|
|
- **Services:** map raw exceptions into internal categories and preserve causal chain (`raise ... from ...`).
|
|
- **UI/API:** map internal category -> canonical envelope category and emit user-safe, actionable messages.
|
|
|
|
## Retry Rules
|
|
|
|
- No auto-retry for `validation`, `not_found`, `conflict`.
|
|
- `external`/`timeout` may be retried when operation semantics are safe.
|
|
- Preserve each retry as new evidence where applicable (no history rewrite).
|
|
|
|
## Job/Page Failure Semantics
|
|
|
|
- Page-level (`JobSource`): `pending`, `transcribed`, `failed`, `cancelled`.
|
|
- Job terminals: `transcribed`, `partial_success`, `failed`.
|
|
- Cancellation must keep job-level and page-level semantics explicit and consistent.
|
|
- Do not emit legacy terminal state language such as `completed` in active user/API lifecycle contracts.
|
|
|
|
## User-Safe Messaging
|
|
|
|
- Never leak stack traces, credentials, auth headers, or local filesystem paths in user-facing output.
|
|
- Include actionable remediation guidance aligned to category.
|
|
- Keep envelope structure consistent across API endpoints.
|
|
|
|
### `AppError.message` vs `AppError.detail`
|
|
|
|
`AppError` carries two texts with different audiences, and they must not be collapsed. Getting this
|
|
wrong has already caused a real defect in this repository, in both directions.
|
|
|
|
| Attribute | Audience | Reaches | Rule |
|
|
| :--- | :--- | :--- | :--- |
|
|
| `message` | User and API clients | `ErrorEnvelope.message`, UI notifications | Stays generic. Never embed exception text, provider payloads, or filesystem paths. |
|
|
| `detail` | Internal only | Logs, and `format_error_detail` -> `ExecutionAttempt.error_detail` and `MaintenanceRun.error_detail` | Carries the root cause. Never rendered to users or serialized into an envelope. |
|
|
|
|
- Putting root-cause data in `message` leaks infrastructure detail to users.
|
|
- Omitting it from `detail` silently degrades the provenance record this system exists to preserve —
|
|
a failed attempt whose `error_detail` says nothing is an attempt that cannot be diagnosed later.
|
|
- When you raise from a caught exception, populate **both**: a generic `message` and a `detail`
|
|
carrying `type(exc).__name__` and the exception text, with `raise ... from exc`.
|
|
- `detail` is optional (`None`). A read path that assumes it is populated must handle its absence.
|
|
- Before changing either attribute, or any helper that formats them, enumerate every consumer —
|
|
evidence writes, maintenance runs, logging, API envelopes, and UI presentation all read these
|
|
fields, and tests assert on the persisted text.
|
|
|
|
Canonical definitions live in `src/transcription/errors.py`; see also `docs/error_handling.md`.
|
|
|
|
## Logging and Diagnostics
|
|
|
|
- Log operation identifiers and error IDs where available.
|
|
- Preserve category + cause-chain context.
|
|
- Distinguish no-response timeout/network failures from returned provider error responses.
|
|
|
|
## Guardrails
|
|
|
|
- No broad catch-and-swallow patterns.
|
|
- No success-shaped fallback values after exceptions.
|
|
- Category mapping must remain deterministic and testable.
|
|
|
|
## Contract Sync Rule
|
|
|
|
If taxonomy, retries, or envelope semantics change:
|
|
|
|
1. Update canonical docs (`docs/error_handling.md`, and invariant docs if needed).
|
|
2. Update tests in the same change.
|
|
3. Update related instruction/skill references.
|
|
4. If change affects persisted status/category fields, update `docs/schema.md` when applicable.
|