Skip to main content

mimick/api_client/
errors.rs

1//! HTTP and network error classification for user-facing diagnostics.
2//!
3//! Maps HTTP status codes and request context into structured `ApiIssue`
4//! values with a summary and guidance string. The settings window and
5//! health dashboard display these to help users self-diagnose problems.
6
7use super::ApiIssue;
8
9/// Identifies the operational context in which an Immich request was made.
10#[derive(Debug, Clone, Copy)]
11pub(super) enum RequestContext {
12    Upload,
13    Albums,
14    AlbumCreate,
15    AlbumAssign,
16    ThumbnailFetch,
17    AssetList,
18    SmartSearch,
19    MetadataSearch,
20    AssetDownload,
21    ServerStats,
22    ServerAbout,
23}
24
25/// Map an HTTP status code to an actionable user-facing `ApiIssue`.
26pub(super) fn classify_http_issue(
27    context: RequestContext,
28    status: u16,
29    subject: Option<&str>,
30) -> ApiIssue {
31    match status {
32        401 | 403 => ApiIssue {
33            summary: "Immich rejected the API key".to_string(),
34            guidance: "Update the API key in Settings and confirm it still has the required Asset and Album permissions (see README for the full list)."
35                .to_string(),
36        },
37        404 if matches!(context, RequestContext::AlbumAssign | RequestContext::AlbumCreate) => {
38            ApiIssue {
39                summary: "An album reference is no longer valid".to_string(),
40                guidance: "Refresh the album list or choose a different album before retrying."
41                    .to_string(),
42            }
43        }
44        413 => ApiIssue {
45            summary: "Immich rejected a file as too large".to_string(),
46            guidance: "Reduce the file size, raise the server upload limit, or skip oversized files with folder rules."
47                .to_string(),
48        },
49        429 => ApiIssue {
50            summary: "Immich rate-limited the request".to_string(),
51            guidance: "Wait a moment and retry. If this happens often, lower upload concurrency or check reverse proxy limits."
52                .to_string(),
53        },
54        502..=504 => ApiIssue {
55            summary: "Immich is temporarily unavailable".to_string(),
56            guidance: "Wait a moment and retry. If it keeps happening, inspect the server and reverse proxy logs."
57                .to_string(),
58        },
59        _ => ApiIssue {
60            summary: match context {
61                RequestContext::Upload => {
62                    format!("Immich could not accept {}", subject.unwrap_or("the upload"))
63                }
64                RequestContext::Albums => "Immich could not load the album list".to_string(),
65                RequestContext::AlbumCreate => format!(
66                    "Immich could not create album '{}'",
67                    subject.unwrap_or("Unnamed")
68                ),
69                RequestContext::AlbumAssign => {
70                    "Immich could not add the asset to the selected album".to_string()
71                }
72                RequestContext::ThumbnailFetch => {
73                    "Immich could not load a library thumbnail".to_string()
74                }
75                RequestContext::AssetList => {
76                    "Immich could not load library assets".to_string()
77                }
78                RequestContext::SmartSearch => {
79                    "Immich could not run the smart library search".to_string()
80                }
81                RequestContext::MetadataSearch => {
82                    "Immich could not run the metadata library search".to_string()
83                }
84                RequestContext::AssetDownload => {
85                    "Immich could not download the selected asset".to_string()
86                }
87                RequestContext::ServerStats => {
88                    "Immich could not load library statistics".to_string()
89                }
90                RequestContext::ServerAbout => {
91                    "Immich could not load server version information".to_string()
92                }
93            },
94            guidance: format!(
95                "The server responded with HTTP {}. Check the server logs and retry after confirming the current configuration.",
96                status
97            ),
98        },
99    }
100}
101
102/// Map a reqwest connection or timeout error to an actionable user-facing `ApiIssue`.
103pub(super) fn classify_network_issue(context: RequestContext, error: &reqwest::Error) -> ApiIssue {
104    if error.is_timeout() {
105        ApiIssue {
106            summary: "The Immich request timed out".to_string(),
107            guidance: "Check network quality and server responsiveness, then retry.".to_string(),
108        }
109    } else if error.is_connect() {
110        ApiIssue {
111            summary: "Could not reach the Immich server".to_string(),
112            guidance: "Check the configured URLs, your network connection, and whether the server is online."
113                .to_string(),
114        }
115    } else {
116        ApiIssue {
117            summary: match context {
118                RequestContext::Upload => "The upload request failed before completion".to_string(),
119                RequestContext::Albums => "The album request failed before completion".to_string(),
120                RequestContext::AlbumCreate => {
121                    "The album creation request failed before completion".to_string()
122                }
123                RequestContext::AlbumAssign => {
124                    "The album assignment request failed before completion".to_string()
125                }
126                RequestContext::ThumbnailFetch => {
127                    "The thumbnail request failed before completion".to_string()
128                }
129                RequestContext::AssetList => {
130                    "The library asset request failed before completion".to_string()
131                }
132                RequestContext::SmartSearch => {
133                    "The smart search request failed before completion".to_string()
134                }
135                RequestContext::MetadataSearch => {
136                    "The metadata search request failed before completion".to_string()
137                }
138                RequestContext::AssetDownload => {
139                    "The asset download request failed before completion".to_string()
140                }
141                RequestContext::ServerStats => {
142                    "The library statistics request failed before completion".to_string()
143                }
144                RequestContext::ServerAbout => {
145                    "The server version request failed before completion".to_string()
146                }
147            },
148            guidance: "Retry the request after checking network connectivity and server health."
149                .to_string(),
150        }
151    }
152}