{"record":{"id":"cfbcf6f887747707","repo":"risingwavelabs/risingwave","slug":"file-sink-only-supports-append-only-mode-at-presen","errorCode":null,"errorMessage":"File sink only supports append-only mode at present. Please change the query to append-only, and specify it explicitly after the `FORMAT ... ENCODE ...` statement. For example, `FORMAT xxx ENCODE xxx(force_append_only='true')`","messagePattern":"File sink only supports append-only mode at present\\. Please change the query to append-only, and specify it explicitly after the `FORMAT \\.\\.\\. ENCODE \\.\\.\\.` statement\\. For example, `FORMAT xxx ENCODE xxx\\(force_append_only='true'\\)`","errorType":"validation","errorClass":"SinkError","httpStatus":null,"severity":"error","filePath":"src/connector/src/sink/file_sink/opendal_sink.rs","lineNumber":153,"sourceCode":"    /// A file is committed only once the batching strategy is met, so an untruncated barrier\n    /// would block the checkpoint forever on the non-decoupled in-memory log store.\n    fn is_sink_decouple(user_specified: &SinkDecouple) -> Result<bool> {\n        match user_specified {\n            SinkDecouple::Default | SinkDecouple::Enable => Ok(true),\n            SinkDecouple::Disable => Err(SinkError::Config(anyhow!(\n                \"File sink can only be created with sink_decouple enabled. Please run `set sink_decouple = true` first.\"\n            ))),\n        }\n    }\n\n    async fn validate(&self) -> Result<()> {\n        if matches!(self.engine_type, EngineType::Snowflake) {\n            risingwave_common::license::Feature::SnowflakeSink\n                .check_available()\n                .map_err(|e| anyhow::anyhow!(e))?;\n        }\n        if !self.is_append_only {\n            return Err(SinkError::Config(anyhow!(\n                \"File sink only supports append-only mode at present. \\\n                    Please change the query to append-only, and specify it \\\n                    explicitly after the `FORMAT ... ENCODE ...` statement. \\\n                    For example, `FORMAT xxx ENCODE xxx(force_append_only='true')`\"\n            )));\n        }\n\n        if self.format_desc.encode != SinkEncode::Parquet\n            && self.format_desc.encode != SinkEncode::Json\n        {\n            return Err(SinkError::Config(anyhow!(\n                \"File sink only supports `PARQUET` and `JSON` encode at present.\"\n            )));\n        }\n\n        match self.op.list(&self.path).await {\n            Ok(_) => Ok(()),\n            Err(e) => Err(anyhow!(e).into()),","sourceCodeStart":135,"sourceCodeEnd":171,"githubUrl":"https://github.com/risingwavelabs/risingwave/blob/6469eb736d691e8e9b8a419a57edd6429ca77417/src/connector/src/sink/file_sink/opendal_sink.rs#L135-L171","documentation":"The file sink currently only supports append-only streams; it cannot process UPDATE or DELETE events. During `validate`, if the sink's input is not append-only (`is_append_only == false`), creation is rejected with guidance to force append-only output via the encode option.","triggerScenarios":"Creating a file sink from a materialized view/table or query that has upsert/delete semantics (primary key present with updates), without specifying `force_append_only = 'true'` in the encode options.","commonSituations":"Sinking a keyed MV that receives UPDATEs to a file sink; forgetting `force_append_only='true'` after FORMAT/ENCODE; migrating an upsert sink definition to a file sink.","solutions":["Add `force_append_only = 'true'` to the ENCODE options, e.g. `FORMAT PLAIN ENCODE JSON(force_append_only = 'true')`","Ensure the input stream is genuinely append-only (use an append-only MV or aggregation without a key)","If updates must be preserved, use a non-file sink (e.g. JDBC/Kafka upsert) instead of a file sink"],"exampleFix":"// before\nCREATE SINK s FROM mv WITH (connector = 's3', type = 'append-only')\nFORMAT PLAIN ENCODE JSON;\n// after\nCREATE SINK s FROM mv WITH (connector = 's3', type = 'append-only')\nFORMAT PLAIN ENCODE JSON(force_append_only = 'true');","handlingStrategy":"validation","validationCode":"-- only sink append-only MVs; or force append-only output:\nCREATE SINK s FROM mv WITH (connector = 's3', ...)\nFORMAT PLAIN ENCODE JSON(force_append_only = 'true');","typeGuard":null,"tryCatchPattern":"match err {\n    e if e.to_string().contains(\"only supports append-only\") => {\n        eprintln!(\"Add force_append_only = 'true' to ENCODE options, or use an append-only source\");\n    }\n    other => return Err(other),\n}","preventionTips":["Add `force_append_only = 'true'` whenever a file sink's input may receive updates","Verify the source MV is append-only (no primary-key UPDATE/DELETE semantics) before creating a file sink","Use upsert-capable sinks (Kafka, JDBC) when updates must be preserved"],"tags":["sink","file-sink","append-only","validation"],"backgroundTag":"unsupported-operation","analyzedSha":"6469eb736d691e8e9b8a419a57edd6429ca77417","analyzedAt":"2026-09-11T21:06:21.487Z","contentChangedAt":"2026-09-11T21:06:21.487Z","schemaVersion":2},"datasetVersion":"2026-09-14T16:17:12.679Z"}