risingwavelabs/risingwave · error
Table ` ` is not backed by a webhook source
Error message
Table `{}` is not backed by a webhook source What it means
acquire_table_info looks up the target table's webhook_info in the catalog; if the table has no webhook source backing it, the handler returns HTTP 403 FORBIDDEN with this message. Webhook ingestion only works for tables created with a webhook-compatible source (e.g. via CREATE TABLE ... WITH (webhook) or a webhook connector).
Solutions
- Verify the table supports webhooks: CREATE TABLE t (...) WITH (webhook = true) or via the webhook connector; recreate with webhook support if missing.
- Check the table name/URL path for typos — the target must be a webhook-backed table.
- Inspect rw_catalog.rw_tables / webhook info to confirm webhook_info exists for the table id.
- If using webhook connections, ensure the connection is bound to the correct table.
Example fix
-- before CREATE TABLE events (id INT); -- no webhook support POST /webhook/table/events ... -- 403 -- after CREATE TABLE events (id INT, payload JSONB) WITH (webhook = true); POST /webhook/table/events ...
Defensive patterns
Strategy: validation
Validate before calling
-- confirm the table is webhook-backed before POSTing SELECT name FROM rw_catalog.rw_tables WHERE name = 'events' AND webhook_info IS NOT NULL;
Try / catch
try {
await postWebhook(`/table/${tableName}`, payload);
} catch (e) {
if (e.status === 403 && e.message.includes('not backed by a webhook source')) {
throw new Error(`configure webhook support on table '${tableName}' first`);
} else { throw e; }
} Prevention
- Create webhook tables explicitly with webhook = true (or the webhook connector).
- Keep a registry of webhook-enabled table names used by producers.
- Verify webhook_info exists in the catalog after any table migration/recreation.
- Check the URL table name for typos — 403 here usually means wrong or non-webhook table.
When it happens
Trigger: POSTing to /webhook/table/<name> where table_catalog.webhook_info is None: the table was created without webhook support, the webhook source was detached/dropped, or the POST targets the wrong (non-webhook) table name.
Common situations: Typos in the table path; attempting webhook writes to a regular MV or plain table; tables created before the webhook feature or migrated catalogs missing webhook_info; using a connection-based webhook against a table without one.
Understand the failure class
Background: Permission denied / not authorized / 403 Forbidden: access-control rejections when the caller lacks the required role, grant, or ownership — this error's family across 18 libraries.
Related errors
AI-assisted analysis of risingwavelabs/risingwave@6469eb736d (2026-09-11).
Data as JSON: /api/errors/75af4cd404b3aee2.
Report an issue: GitHub.
Appendix: source
Thrown at src/frontend/src/webhook/mod.rs:263
let (columns_to_insert, row_id_index) = table_catalog.columns_to_insert();
let payload_schema = PayloadSchema::new(
columns_to_insert
.map(|(column, is_pk)| WebhookTableColumnDesc {
is_pk,
name: column.column_desc.name.clone(),
data_type: column.column_desc.data_type.clone(),
})
.collect(),
);
let row_id_index = row_id_index.map(|row_id_index| row_id_index as u32);
let webhook_source_info = table_catalog
.webhook_info
.as_ref()
.ok_or_else(|| {
err(
anyhow!("Table `{}` is not backed by a webhook source", table),
StatusCode::FORBIDDEN,
)
})?
.clone();
(
webhook_source_info,
table_catalog.id(),
table_catalog.version_id().expect("table must be versioned"),
row_id_index,
payload_schema,
)
};
let compute_client = choose_fast_insert_client(table_id, frontend_env, request_id)
.await
.map_err(|e| {
err(
anyhow!(e).context("Failed to choose a compute node for fast insert"),View on GitHub (pinned to 6469eb736d)