neondatabase/neon · warning
Failed to capture events: {}, {}
Error message
Failed to capture events: {}, {} What it means
capture_event POSTs a single event {api_key, distinct_id, event, properties} to {public_api_url}/capture/ using the client_api_key. Any non-2xx response produces this error with the HTTP status and PostHog's response body. The body typically names the concrete problem: invalid API key, invalid JSON/property types, or payload-size limits.
Source
Thrown at libs/posthog_client_lite/src/lib.rs:627
) -> anyhow::Result<()> {
// PUBLIC_URL/capture/
let url = format!("{}/capture/", self.config.public_api_url);
let response = self
.client
.post(url)
.body(serde_json::to_string(&json!({
"api_key": self.config.client_api_key,
"distinct_id": distinct_id,
"event": event,
"properties": properties,
}))?)
.send()
.await?;
let status = response.status();
let body = response.text().await?;
if !status.is_success() {
return Err(anyhow::anyhow!(
"Failed to capture events: {}, {}",
status,
body
));
}
Ok(())
}
pub async fn capture_event_batch(&self, events: &[CaptureEvent]) -> anyhow::Result<()> {
// PUBLIC_URL/batch/
let url = format!("{}/batch/", self.config.public_api_url);
let response = self
.client
.post(url)
.body(serde_json::to_string(&json!({
"api_key": self.config.client_api_key,
"batch": events,
}))?)
.send()View on GitHub (pinned to 8f60b04da4)
Solutions
- Check the embedded body text: fix the client_api_key for 401, the properties shape for 400
- Ensure public_api_url points at the capture endpoint of the correct PostHog region/project
- Shrink or split very large property payloads (move big blobs out of event properties)
- Treat 5xx as transient: retry with backoff or drop the event (it is telemetry), but log the body
Defensive patterns
Strategy: retry
Validate before calling
// Only send events you know are well-formed; cheap client-side checks avoid 400s:
fn event_is_well_formed(event: &str, distinct_id: &str, props: &serde_json::Value) -> bool {
!event.is_empty()
&& !distinct_id.is_empty()
&& props.is_object() // posthog expects an object of properties
&& serde_json::to_string(props).map(|s| s.len() < 512 * 1024).unwrap_or(false)
} Try / catch
// Telemetry must never take down the caller: retry transient, swallow-and-log the rest.
let mut bo = ExponentialBackoff::default();
loop {
match client.capture_event(event, distinct_id, &props).await {
Ok(()) => break,
Err(e) => {
let msg = format!("{e:#}");
let transient = msg.contains(" 5") || msg.contains("timeout") || msg.contains(" 429, ");
if !transient || bo.next_backoff().is_none() {
tracing::warn!("posthog capture dropped: {msg}");
break; // analytics is best-effort
}
}
}
} Prevention
- Treat capture as best-effort: wrap calls so failures log-and-drop instead of propagating
- Unit-test event producers for non-empty names/ids and JSON-serializable properties
- Keep properties small and primitive; never embed blobs, logs, or payloads in events
- Alert on capture error rate, not on individual errors -- a rising 401 signals key rotation
When it happens
Trigger: 401 'invalid API key' when client_api_key is wrong or from a different project; 400 when properties contain unserializable/wrong-typed values or the event name is empty; 413 when a huge properties payload exceeds the capture limit; 5xx during PostHog incidents.
Common situations: Using the server/personal key where the project API key is required (or vice versa); events sent to the wrong region (EU vs US public_api_url); oversized batch-like properties on a single event; proxy/WAF rejecting the JSON POST.
Related errors
- setting scheduling policy unsuccessful for safekeeper {node_
- Failed to get feature flags: {}, {}
- error downloading extension {:?}: {:?}
- Exhausted all attempts to retrieve the config from the contr
- Failed to check node status: {e}
AI-assisted analysis of neondatabase/neon@8f60b04da4 (2026-08-16).
Data as JSON: /api/errors/b095364d856e649a.
Report an issue: GitHub.