risingwavelabs/risingwave · error
`SECURE_COMPARE()` failed
Error message
`SECURE_COMPARE()` failed
What it means
SECURE_COMPARE is the internal expression used to compare the expected and provided webhook signatures. If its row evaluation returns NULL (an Option::None result), RisingWave maps that to this 400 BAD_REQUEST error. This typically means one of the comparison inputs was NULL, e.g. the client sent no signature.
Solutions
- Always send the signature header with the request.
- Verify the secret expression/column in the table definition is not NULL.
- Check the payload for fields the signature depends on being present.
- Treat as 400: fix the request rather than retrying.
Example fix
// before
curl -X POST http://host/v1/tables/123/webhook -d '{...}' # no signature header
// after
curl -X POST http://host/v1/tables/123/webhook -H "x-webhook-signature: <hmac>" -d '{...}' Defensive patterns
Strategy: try-catch
Validate before calling
if (!headers.get('signature')) throw new Error('signature header is required for webhook requests'); Try / catch
const res = await post(url, body, headers);
if (res.status === 400 && (await res.text()).includes('SECURE_COMPARE')) {
// signature input was NULL: attach the signature header and retry once
} Prevention
- Never omit the signature header on webhook requests.
- Ensure the secret column/expression in the table definition is never NULL.
- Validate the payload against the schema before sending so signature inputs are present.
When it happens
Trigger: verify_signature evaluates the SECURE_COMPARE expression but eval_row returns Ok(None) — usually when the signature header is absent or one operand of the comparison is NULL.
Common situations: Client omits the signature header entirely, secret material resolving to NULL, misconfigured webhook table definition where the signature column/expression evaluates to NULL.
Related errors
- Signature verification failed
- adlsgen2.authority_host must not contain userinfo
- adlsgen2.authority_host must use the https scheme, got
- anyhow!(e)
- snapshot primary key ` ` cannot be NULL
AI-assisted analysis of risingwavelabs/risingwave@6469eb736d (2026-09-11).
Data as JSON: /api/errors/915df0daa9cea105.
Report an issue: GitHub.
Appendix: source
Thrown at src/frontend/src/webhook/utils.rs:156
payload: &[u8],
signature_expr: ExprNode,
) -> Result<bool> {
let row = OwnedRow::new(vec![
Some(headers_jsonb.into()),
Some(secret.into()),
Some(payload.into()),
]);
let signature_expr_impl = ExprImpl::from_expr_proto(&signature_expr)
.map_err(|e| err(e, StatusCode::INTERNAL_SERVER_ERROR))?;
let result = signature_expr_impl
.eval_row(&row)
.await
.map_err(|e| err(e, StatusCode::INTERNAL_SERVER_ERROR))?
.ok_or_else(|| {
err(
anyhow!("`SECURE_COMPARE()` failed"),
StatusCode::BAD_REQUEST,
)
})?;
Ok(*result.as_bool())
}
#[cfg(test)]
mod tests {
use axum::body::to_bytes;
use axum::http::header::HeaderName;
use super::*;
#[tokio::test]
async fn test_webhook_error_response() {
let response = err(
anyhow!("failed to decode webhook payload"),
StatusCode::UNPROCESSABLE_ENTITY,View on GitHub (pinned to 6469eb736d)