stalwartlabs/stalwart · error
StoreEvent::HttpStoreError
StoreEvent::HttpStoreError
Error message
Failed to fetch HTTP list
What it means
The HTTP-backed store (used e.g. for IP blocklists/allowlists) refreshed its list by fetching a configured URL and the server returned a non-2xx status. The error carries the HTTP status code, URL and elapsed time as context. `try_refresh` bails so the caller (`refresh`) can keep the previous list or retry later.
Source
Thrown at crates/store/src/backend/http/lookup.rs:110
let time = Instant::now();
let agent = BROWSER_USER_AGENTS.choose(&mut rand::rng()).unwrap();
let response = self
.client
.get(&self.config.url)
.timeout(self.config.timeout)
.header(reqwest::header::USER_AGENT, *agent)
.send()
.await
.map_err(|err| {
trc::StoreEvent::HttpStoreError
.into_err()
.reason(err)
.ctx(trc::Key::Url, self.config.url.to_compact_string())
.details("Failed to build request")
})?;
if !response.status().is_success() {
trc::bail!(
trc::StoreEvent::HttpStoreError
.into_err()
.ctx(trc::Key::Code, response.status().as_u16())
.ctx(trc::Key::Url, self.config.url.to_compact_string())
.ctx(trc::Key::Elapsed, time.elapsed())
.details("Failed to fetch HTTP list")
);
}
let bytes = response
.bytes_with_limit(self.config.max_size)
.await
.map_err(|err| {
trc::StoreEvent::HttpStoreError
.into_err()
.reason(err)
.ctx(trc::Key::Url, self.config.url.to_compact_string())
.ctx(trc::Key::Elapsed, time.elapsed())View on GitHub (pinned to e962003857)
Solutions
- Check the URL in the error context returns 200 when fetched manually (curl -I).
- Fix the configured URL in config (http store url setting) if it is wrong or moved.
- Add required authentication/credentials expected by the list provider.
- Verify network/proxy reachability to the provider and check for rate limiting (status 429).
- Confirm the list service is up; rely on the cached list until it recovers.
Example fix
// before (config.toml) url = "http://example.com/blocklist.txt" // after url = "https://lists.example.net/v2/blocklist.txt?apikey=YOUR_KEY"
Defensive patterns
Strategy: retry
Validate before calling
// before configuring, verify the URL serves the list
// curl -fsS -o /dev/null -w '%{http_code}' "$URL" # expect 200 Try / catch
// retry transient failures, keep last-good list otherwise
match refresh().await {
Err(err) if is_transient(&err) => schedule_retry(err, backoff),
Err(err) => log::warn!("keeping cached list: {err}"),
Ok(_) => {},
} Prevention
- Monitor the list URL with an uptime check.
- Include API keys/credentials in store config where the provider requires them.
- Keep the previously fetched list cached so failures degrade gracefully.
- Pin list URLs to stable, versioned endpoints.
When it happens
Trigger: Periodic or manual refresh issues a GET to `self.config.url` and `response.status().is_success()` is false (404, 403, 500, etc.).
Common situations: Misconfigured list URL; the list provider requires an API key that's missing/expired; provider outage or rate-limiting; self-hosted list server down.
Understand the failure class
Background: "API error: {status}" and "HTTP 401/403/404/429/5xx" errors: non-2xx HTTP responses explained — this error's family across 27 libraries.
Related errors
- OpenAPI request failed
- unwrap_tls called on non-TLS acceptor
- Cluster::PublisherError
- Cluster::SubscriberError
- Cluster::PublisherError
AI-assisted analysis of stalwartlabs/stalwart@e962003857 (2026-09-06).
Data as JSON: /api/errors/ca9a74ec865cd2a2.
Report an issue: GitHub.