Every error response in the { error, code, details? } format (step 2)

Moves every remaining hand-built error response onto ApiError: ~300
sites across faces, image, video, files, memories, versions,
duplicates, favorites, libraries, auth, clip/unified search, prefs,
reels, tts, insight chat, persona chat, knowledge and personas.

- 500s: the body was the error's text (anyhow / diesel / {:?}); now
  ApiError::internal logs it and sends a generic message.
- Each 4xx/502/503 has a stable code and a message written for users;
  statuses are unchanged. Shared helpers per module (faces_unavailable,
  person_not_found, unknown_library, insight_not_found, ...).
- Pre-format top-level fields kept where clients read them: upload 409
  duplicate_of / content_hash / library_name.
- PersonaChatError converts into ApiError in one place.
- Multipart upload errors use the format (error::multipart_config).
- Guard test: no non-test code builds HttpResponse::<error>() by hand.

Closes #202

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Cameron CordesandClaude Opus 5.5 committed 2026-11-01 13:29:13 -05:00
1 parent 1836f1b9ca
commit 431c3a7492
23 files changed
+894 -542

No files matched your search

+9
View File
@@ -797,6 +797,15 @@ GET /timezones (IANA zone list from chrono-tz; unauthentic
**Service Builder Pattern:**
Routes are registered via composable `ServiceBuilder` trait in `service.rs`. Allows modular feature addition.
**Error responses (`src/error.rs`, #202):**
Every error body is `{"error": "<message safe to show a user>", "code": "<stable_snake_case>", "details"?: {...}}`.
Build them with `ApiError` (`bad_request` / `not_found` / `conflict` / `unavailable` / `bad_gateway` / ... `(code, message)`),
returned as `Err(..)` from an `ApiResult` handler or via `.into_response()`. Unexpected failures go through
`ApiError::internal(cause)` (or `?` on anyhow / diesel): a 500 `internal` whose cause is logged, never sent, since it can
hold SQL and library paths. Never rename a code a client may branch on; add a new one. Don't put file paths in `details`.
Request-parsing failures, the auth extractor, multipart uploads and unknown routes use the format too (configs in
`error.rs`, registered in `main.rs`). A test fails if a handler builds `HttpResponse::NotFound()` etc. by hand.
**Path Validation:**
Always use `is_valid_full_path(&base_path, &requested_path, check_exists)` to prevent directory traversal attacks.