risingwavelabs/risingwave · error · SinkError::Http
Turbopuffer sink received non-success response
Error message
Turbopuffer sink received non-success response: {} {} What it means
send_turbopuffer_request performs the HTTP POST/DELETE to the Turbopuffer v2 API and requires a 2xx status. If the API returns any non-success status, the error includes the HTTP status code and the response body text. This surfaces auth failures, invalid payloads, quota issues, and Turbopuffer-side validation errors.
Solutions
- Read the status and body in the error message to identify the cause (401/403 → fix the API key; 400 → fix schema/payload; 429 → reduce throughput).
- Verify the API key and that the namespace/project are correct.
- Check that column types in the sink match the Turbopuffer namespace schema; drop or recreate the namespace if the schema changed.
- Retry with backoff for 429/5xx responses.
Example fix
// before: expired key WITH (connector='turbopuffer', turbopuffer.api_key='sk-old', ...) // after: valid key via secret WITH (connector='turbopuffer', turbopuffer.api_key=secret turbopuffer_api_key, ...)
Defensive patterns
Strategy: retry
Validate before calling
// before creating the sink, verify credentials with a direct API call:
// curl -s -o /dev/null -w '%{http_code}' -H "Authorization: Bearer $TPUF_KEY" https://api.turbopuffer.com/v1/namespaces Try / catch
match res {
Ok(()) => {}
Err(e) if e.to_string().contains("non-success response: 429") => retry_with_backoff(e),
Err(e) => return Err(e), // 4xx: do not retry, fix key/schema
} Prevention
- Store the API key as a RisingWave secret, not inline.
- Keep sink column types in sync with the Turbopuffer namespace schema.
- Monitor rate limits; tune sink throughput/flush intervals for heavy streams.
When it happens
Trigger: flush_all sends a buffered write/delete payload and the Turbopuffer API responds with a non-2xx status (401 wrong API key, 400 malformed payload/schema mismatch, 429 rate limited, 5xx server error).
Common situations: Expired or wrong TURBOPUFFER_API_KEY; schema in the upsert not matching an existing namespace's schema; exceeding rate limits during heavy streaming writes; network/proxy returning error pages.
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
AI-assisted analysis of risingwavelabs/risingwave@6469eb736d (2026-09-11).
Data as JSON: /api/errors/8d082b32939fec92.
Report an issue: GitHub.
Appendix: source
Thrown at src/connector/src/sink/turbopuffer.rs:678
}))
.await?;
Ok(())
}
}
async fn send_turbopuffer_request(client: reqwest::Client, url: String, body: Value) -> Result<()> {
let resp = client
.post(url)
.json(&body)
.send()
.await
.context("turbopuffer write request failed")
.map_err(SinkError::Http)?;
if !resp.status().is_success() {
let status = resp.status();
let body = resp.text().await.unwrap_or_default();
return Err(SinkError::Http(anyhow!(
"Turbopuffer sink received non-success response: {} {}",
status,
body
)));
}
Ok(())
}
#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize)]
#[serde(untagged)]
enum DocumentId {
U64(u64),
String(String),
}
#[derive(Debug)]
enum CompactedOp {
Upsert(Map<String, Value>),View on GitHub (pinned to 6469eb736d)