immich-app/immich · error · URLError
URLError.badURL
Error message
URLError.badURL
What it means
The widget's random-search call builds its request URL from the shared server configuration; when buildRequestURL cannot construct a valid URL for the /search/random endpoint it throws URLError(.badURL).
Solutions
- Ensure the main app writes a valid, normalized serverUrl into the shared app-group config before widget use
- Validate URL formation (scheme + host) at save time and on widget launch
- Fall back to a configuration-required widget state when serverConfig is invalid
Example fix
// before
throw URLError(.badURL) // silent misconfig
// after
guard let serverUrl = serverConfig.serverUrl, URL(string: serverUrl)?.host != nil else {
return .failure(.needsConfiguration) // explicit widget state
} Defensive patterns
Strategy: validation
Validate before calling
let canFetch: Bool {
guard let raw = serverConfig.serverUrl,
let url = URL(string: raw),
let scheme = url.scheme,
["http", "https"].contains(scheme),
url.host != nil else { return false }
return true
} Type guard
func isValidServerURL(_ raw: String?) -> Bool {
guard let raw, let url = URL(string: raw), url.host != nil else { return false }
return ["http", "https"].contains(url.scheme ?? "")
} Try / catch
do {
let results = try await api.randomSearch(...)
} catch URLError.badURL {
widgetState = .needsConfiguration
} Prevention
- Validate server URL (scheme + host) when saving config
- Keep widget config in the shared app group in sync with the app
- Show a setup state instead of fetching with invalid config
When it happens
Trigger: randomSearch invoked while serverConfig lacks a usable serverUrl (nil/empty/invalid), making buildRequestURL return nil for the /search/random endpoint.
Common situations: Widget credentials/server config never synced into the app group; user logged out in the main app leaving stale/blank config; server URL entered without scheme.
Understand the failure class
Background: "Invalid URL" / "URL cannot be empty": fix the malformed or missing URL behind request-construction failures — this error's family across 50 libraries.
Related errors
- Device ' ' does not exist. If using Docker, make sure this…
- Failed to add job handler for
- Failed to find job handler for Job.
- Failed to verify SMTP configuration
- Geodata file not found
AI-assisted analysis of immich-app/immich@e55ac299a4 (2026-09-15).
Data as JSON: /api/errors/c6b03ee566a8709d.
Report an issue: GitHub.
Appendix: source
Thrown at mobile/ios/WidgetExtension/ImmichAPI.swift:189
func applyCustomHeaders(for request: inout URLRequest) {
for (header, value) in serverConfig.customHeaders {
request.addValue(value, forHTTPHeaderField: header)
}
}
func fetchSearchResults(with filters: SearchFilter = Album.NONE.filter)
async throws
-> [Asset]
{
// get URL
guard
let searchURL = buildRequestURL(
serverConfig: serverConfig,
endpoint: "/search/random"
)
else {
throw URLError(.badURL)
}
var request = URLRequest(url: searchURL)
request.httpMethod = "POST"
request.httpBody = try JSONEncoder().encode(filters)
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
applyCustomHeaders(for: &request)
let (data, _) = try await URLSession.shared.data(for: request)
// decode data
return try JSONDecoder().decode([Asset].self, from: data)
}
func fetchMemory(for date: Date) async throws -> [MemoryResult] {
// get URL
let localDay = date.formatted(
Date.ISO8601FormatStyle(timeZone: .current).year().month().day().dateSeparator(.dash)View on GitHub (pinned to e55ac299a4)