risingwavelabs/risingwave · error · SinkError::Config
Turbopuffer attribute column must not be named id
Error message
Turbopuffer attribute column must not be named id
What it means
Turbopuffer treats `id` specially in write requests as the document ID, so it cannot be sent as a regular attribute. The sink reserves the primary-key/id column and, in `try_from`, rejects any remaining attribute column literally named `id`.
Source
Thrown at src/connector/src/sink/turbopuffer.rs:221
(None, None) => {
return Err(SinkError::Config(anyhow!(
"Turbopuffer sink requires either namespace or namespace_column"
)));
}
};
// Turbopuffer treats `id` as the document ID in write requests; it is not a schema
// attribute. Dynamic namespace is also metadata for routing, not a document attribute.
let excluded_indices = match &namespace {
TurbopufferNamespace::Static(_) => HashSet::from([pk_index]),
TurbopufferNamespace::Dynamic { index } => HashSet::from([pk_index, *index]),
};
let attribute_indices = (0..schema.len())
.filter(|idx| !excluded_indices.contains(idx))
.collect_vec();
for index in &attribute_indices {
if schema[*index].name == "id" {
return Err(SinkError::Config(anyhow!(
"Turbopuffer attribute column must not be named id"
)));
}
}
let full_text_search_columns = parse_column_selection(
config.full_text_search_columns.as_deref(),
&schema,
&attribute_indices,
)?;
let filterable_columns = parse_column_selection(
config.filterable_columns.as_deref(),
&schema,
&attribute_indices,
)?;
let has_vector = attribute_indices
.iter()
.any(|idx| matches!(schema[*idx].data_type, DataType::Vector(_)));
if has_vector && config.distance_metric.is_none() {View on GitHub (pinned to 6469eb736d)
Solutions
- Rename the attribute column (e.g. `record_id`, `item_id`) in the materialization feeding the sink.
- Make the `id` column the sink's primary key so it is consumed as the document ID instead of an attribute.
- Drop the redundant `id` column from the sink schema if it duplicates the pk.
- Alias it during SELECT, e.g. `id AS external_id` in the upstream MV.
Example fix
// before CREATE SINK s FROM mv WITH (connector='turbopuffer', pk='doc_pk'); -- mv has both doc_pk (pk) and id (attribute) // after CREATE MATERIALIZED VIEW mv2 AS SELECT doc_pk, id AS external_id, ... FROM mv; CREATE SINK s FROM mv2 WITH (connector='turbopuffer', pk='doc_pk');
Defensive patterns
Strategy: validation
Validate before calling
// SQL: ensure no attribute column is literally 'id' SELECT name FROM rw_columns WHERE relation = 'mv_for_sink' AND name = 'id'; // must return no rows unless 'id' is the designated pk
Prevention
- Rename source `id` columns (e.g. external_id) before sinking to Turbopuffer
- Designate `id` as the sink primary key if it should be the document ID
- Lint sink schemas for reserved names at deploy time
When it happens
Trigger: Creating a Turbopuffer sink whose schema contains a column named `id` that is not the primary key designated for the document ID (it is neither excluded nor used as pk), e.g. an extra `id` varchar column distinct from the pk column.
Common situations: Source tables carry their own `id` column while the user also defines a separate pk for the sink; renaming a source column to `id` during schema evolution; copying tables that already have an `id` attribute.
Understand the failure class
Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.
Related errors
- Turbopuffer namespace_column must be varchar, got {:?}
- Turbopuffer sink requires only one of namespace or namespace
- Turbopuffer sink requires either namespace or namespace_colu
- Turbopuffer sink requires distance_metric when sink schema c
- Turbopuffer sink can only be created with sink_decouple enab
AI-assisted analysis of risingwavelabs/risingwave@6469eb736d (2026-09-11).
Data as JSON: /api/errors/20f07488ebdb2f8e.
Report an issue: GitHub.