risingwavelabs/risingwave · error · SinkError::Config

HTTP sink payload column must be varchar or jsonb, got {:?}

Error message

HTTP sink payload column must be varchar or jsonb, got {:?}

What it means

The HTTP sink sends the `payload` column as the request body, so it must be a string (VARCHAR) or JSONB that can be serialized. `validate_http_sink` rejects the sink if the payload column has any other type (e.g. INT, struct, bytea).

Source

Thrown at src/connector/src/sink/http.rs:193

                    return Err(SinkError::Config(anyhow!(
                        "HTTP sink url column must be varchar, got {:?}",
                        fields[url_index].data_type
                    )));
                }
                HttpUrl::Dynamic { url_index }
            }
            (None, None) => {
                return Err(SinkError::Config(anyhow!(
                    "HTTP sink requires either url option or url column"
                )));
            }
        };

        (payload_index, url, fields[payload_index].data_type.clone())
    };

    if payload_type != DataType::Varchar && payload_type != DataType::Jsonb {
        return Err(SinkError::Config(anyhow!(
            "HTTP sink payload column must be varchar or jsonb, got {:?}",
            payload_type
        )));
    }

    let mut header_map = HeaderMap::new();
    header_map.insert(
        CONTENT_TYPE,
        content_type
            .unwrap_or(match payload_type {
                DataType::Varchar => "text/plain",
                DataType::Jsonb => "application/json",
                _ => unreachable!("validated HTTP sink column type"),
            })
            .parse()
            .context("invalid content_type")
            .map_err(SinkError::Config)?,
    );

View on GitHub (pinned to 6469eb736d)

Solutions

  1. Cast the payload column in the sink query: SELECT payload::JSONB AS payload ... or payload::VARCHAR
  2. Change the underlying table column type to VARCHAR or JSONB
  3. Serialize complex types to JSONB before sinking

Example fix

// before
SELECT payload FROM t; -- payload is INT
// after
SELECT payload::VARCHAR AS payload FROM t; -- or payload::JSONB
Defensive patterns

Strategy: validation

Validate before calling

if let Some(f) = fields.iter().find(|f| f.name == "payload") {
    let ok = f.data_type == DataType::Varchar || f.data_type == DataType::Jsonb;
    assert!(ok, "payload column must be VARCHAR or JSONB, got {:?}", f.data_type);
}

Type guard

fn is_valid_payload(f: &Field) -> bool {
    matches!(f.data_type, DataType::Varchar | DataType::Jsonb)
}

Prevention

When it happens

Trigger: Creating an HTTP sink whose `payload` column (located by name at `payload_index`) is typed as anything other than VARCHAR or JSONB — checked in `try_from` -> `validate_http_sink` after URL resolution.

Common situations: Payload column typed as INT/BYTEA/struct in the table; users expecting the sink to stringify arbitrary types; schema drift after ALTER TABLE changes the payload column type.

Understand the failure class

Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.

Related errors


AI-assisted analysis of risingwavelabs/risingwave@6469eb736d (2026-09-11). Data as JSON: /api/errors/f4ba23b14b4a945d. Report an issue: GitHub.