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

  1. Verify the table supports webhooks: CREATE TABLE t (...) WITH (webhook = true) or via the webhook connector; recreate with webhook support if missing.
  2. Check the table name/URL path for typos — the target must be a webhook-backed table.
  3. Inspect rw_catalog.rw_tables / webhook info to confirm webhook_info exists for the table id.
  4. 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

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)