risingwavelabs/risingwave · error
failed to build webhook JSON decoder
Error message
failed to build webhook JSON decoder
What it means
The `JsonAccessBuilder` — the decoder that turns the webhook JSON body into column accessors — could not be constructed from the request's decoder configuration headers (`x-rw-webhook-*`). `JsonAccessBuilder::new` validates the supplied JsonProperties; an invalid combination fails construction and the request is rejected with HTTP 500. The message is a wrapper; the inner error names the offending option.
Source
Thrown at src/frontend/src/webhook/payload.rs:40
};
use super::WebhookTableColumnDesc;
use super::utils::{Result, err};
const WEBHOOK_FORMAT_HEADER: &str = "x-rw-webhook-format";
const WEBHOOK_ENCODE_HEADER: &str = "x-rw-webhook-encode";
const WEBHOOK_JSON_TIMESTAMP_HANDLING_HEADER: &str = "x-rw-webhook-json-timestamp-handling-mode";
const WEBHOOK_JSON_TIMESTAMPTZ_HANDLING_HEADER: &str =
"x-rw-webhook-json-timestamptz-handling-mode";
const WEBHOOK_JSON_TIME_HANDLING_HEADER: &str = "x-rw-webhook-json-time-handling-mode";
const WEBHOOK_JSON_BIGINT_UNSIGNED_HANDLING_HEADER: &str =
"x-rw-webhook-json-bigint-unsigned-handling-mode";
const WEBHOOK_JSON_HANDLE_TOAST_COLUMNS_HEADER: &str = "x-rw-webhook-json-handle-toast-columns";
pub(crate) fn build_json_access_builder(headers: &HeaderMap) -> Result<JsonAccessBuilder> {
JsonAccessBuilder::new(json_properties_from_headers(headers)?).map_err(|e| {
err(
anyhow!(e).context("failed to build webhook JSON decoder"),
StatusCode::INTERNAL_SERVER_ERROR,
)
})
}
pub(crate) fn owned_row_from_payload_row(
access_builder: &mut JsonAccessBuilder,
columns: &[WebhookTableColumnDesc],
payload_row: &[u8],
) -> Result<OwnedRow> {
let access = access_builder
.generate_json_access(payload_row.to_vec())
.map_err(|e| {
err(
anyhow!(e).context("failed to decode webhook JSON payload"),
StatusCode::UNPROCESSABLE_ENTITY,
)
})?;View on GitHub (pinned to 6469eb736d)
Solutions
- Read the inner error message to see which decoder option was rejected.
- Send only well-formed `x-rw-webhook-json-*` headers, or omit them entirely to use defaults (plain JSON).
- Validate header values against the supported modes (timestamp handling `milli`/`guess_number_unit`, encode `json`, format `plain`).
- Pin the producer's header set to a RisingWave version and re-check after upgrades.
- Test with a minimal curl request containing no custom headers to isolate the cause.
Example fix
// before (invalid header)
headers: { 'x-rw-webhook-json-supported-types': 'nubmer' }
// after (fixed or removed)
headers: { 'x-rw-webhook-format': 'plain', 'x-rw-webhook-encode': 'json' } Defensive patterns
Strategy: validation
Validate before calling
const OK = {
'x-rw-webhook-format': v => !v || String(v).toLowerCase() === 'plain',
'x-rw-webhook-encode': v => !v || String(v).toLowerCase() === 'json'
};
for (const [h, ok] of Object.entries(OK))
if (!ok(req.headers[h])) throw new Error(`invalid webhook header ${h}: ${req.headers[h]}`); Type guard
const isPlainJsonHeaders = (h) => (!h['x-rw-webhook-format'] || String(h['x-rw-webhook-format']).toLowerCase() === 'plain') && (!h['x-rw-webhook-encode'] || String(h['x-rw-webhook-encode']).toLowerCase() === 'json');
Try / catch
try {
const res = await fetch(url, { method: 'POST', headers, body });
if (res.status === 500) console.error('decoder config rejected:', await res.text());
} catch (e) { /* surface decoder option errors */ } Prevention
- Keep the custom x-rw-webhook-* headers minimal; rely on defaults
- Validate header values against documented supported modes before deploy
- Re-validate headers after RisingWave version upgrades
- Log the full header set when webhook requests fail
When it happens
Trigger: POST carrying invalid/contradictory `x-rw-webhook-json-*` headers (e.g. malformed `x-rw-webhook-json-supported-types`, invalid bigint handling mode) so `JsonAccessBuilder::new(json_properties_from_headers(headers))` fails.
Common situations: A producer SDK or proxy adds decoder headers with wrong values or typos; header semantics changed after a RisingWave upgrade; hand-crafted curl requests copy an option name incorrectly.
Understand the failure class
Background: "Invalid value" and "allowed values are" config errors: what your library rejected and how to fix it — this error's family across 41 libraries.
Related errors
- confluent registry parse resp error: {0}
- Fast insert failed: {}
- failed to parse request body
- failed to decode webhook JSON payload
- Vault API returned error status: {} - {}
AI-assisted analysis of risingwavelabs/risingwave@6469eb736d (2026-09-11).
Data as JSON: /api/errors/f176de4b7c0669a4.
Report an issue: GitHub.