{"record":{"id":"3596994373ef00f7","repo":"risingwavelabs/risingwave","slug":"turbopuffer-full-text-search-column-must-be-s","errorCode":null,"errorMessage":"Turbopuffer full_text_search column '{}' must be string or []string","messagePattern":"Turbopuffer full_text_search column '(.+?)' must be string or \\[\\]string","errorType":"validation","errorClass":"SinkError::Config","httpStatus":null,"severity":"error","filePath":"src/connector/src/sink/turbopuffer.rs","lineNumber":791,"sourceCode":"    Ok(columns)\n}\n\nfn build_turbopuffer_schema(\n    schema: &Schema,\n    attribute_indices: &[usize],\n    full_text_search_columns: &HashSet<String>,\n    filterable_columns: &HashSet<String>,\n) -> Result<Value> {\n    let mut result = Map::new();\n    for index in attribute_indices {\n        let field = &schema[*index];\n        let mut config = Map::new();\n        let data_type = field.data_type();\n        let turbopuffer_type = turbopuffer_type(&data_type)?;\n        let is_vector = matches!(data_type, DataType::Vector(_));\n        let is_full_text_search = full_text_search_columns.contains(&field.name);\n        if is_full_text_search && !supports_full_text_search(&data_type) {\n            return Err(SinkError::Config(anyhow!(\n                \"Turbopuffer full_text_search column '{}' must be string or []string\",\n                field.name\n            )));\n        }\n        config.insert(\"type\".to_owned(), Value::String(turbopuffer_type));\n        if filterable_columns.contains(&field.name) {\n            config.insert(\"filterable\".to_owned(), Value::Bool(true));\n        }\n        if is_full_text_search {\n            config.insert(\"full_text_search\".to_owned(), Value::Bool(true));\n        }\n        if is_vector {\n            config.insert(\"ann\".to_owned(), Value::Bool(true));\n        }\n        result.insert(field.name.clone(), Value::Object(config));\n    }\n    Ok(Value::Object(result))\n}","sourceCodeStart":773,"sourceCodeEnd":809,"githubUrl":"https://github.com/risingwavelabs/risingwave/blob/6469eb736d691e8e9b8a419a57edd6429ca77417/src/connector/src/sink/turbopuffer.rs#L773-L809","documentation":"build_turbopuffer_schema validates that every column marked with turbopuffer.full_text_search is either VARCHAR or VARCHAR[] ([]string), since Turbopuffer full-text search indexes only string attributes. Any other data type marked for full-text search is rejected when building the sink schema.","triggerScenarios":"Creating a Turbopuffer sink with turbopuffer.full_text_search='col' where col is an INT, BOOLEAN, TIMESTAMP, JSONB, etc. rather than VARCHAR or VARCHAR[].","commonSituations":"Marking a numeric or timestamp column for full-text search by mistake; expecting automatic text extraction from non-string types.","solutions":["Change the column to VARCHAR or VARCHAR[] in the source/materialized view.","Cast the column to VARCHAR in an intermediate materialized view before sinking.","Remove the column from turbopuffer.full_text_search if full-text indexing is not needed."],"exampleFix":"// before: ts TIMESTAMP marked for FTS\nWITH (turbopuffer.full_text_search='description,ts')\n// after: cast to string first\nCREATE MVIEW mv AS SELECT description, ts::VARCHAR AS ts FROM src;\nWITH (turbopuffer.full_text_search='description,ts')","handlingStrategy":"validation","validationCode":"fn fts_type_ok(dt: &str) -> bool {\n    dt == \"VARCHAR\" || dt == \"VARCHAR[]\" || dt == \"character varying\" || dt == \"character varying[]\"\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Only mark VARCHAR/VARCHAR[] columns with turbopuffer.full_text_search.","Cast numeric/timestamp columns to VARCHAR in a materialized view if text indexing is needed.","Validate the full_text_search list against column types before CREATE SINK."],"tags":["turbopuffer","full-text-search","type-mismatch","config"],"backgroundTag":"type-mismatch","analyzedSha":"6469eb736d691e8e9b8a419a57edd6429ca77417","analyzedAt":"2026-09-11T21:06:21.487Z","contentChangedAt":"2026-09-11T21:06:21.487Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}