risingwavelabs/risingwave · error · SinkError

File sink only supports append-only mode at present. Please

Error message

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')`

What it means

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.

Source

Thrown at src/connector/src/sink/file_sink/opendal_sink.rs:153

    /// A file is committed only once the batching strategy is met, so an untruncated barrier
    /// would block the checkpoint forever on the non-decoupled in-memory log store.
    fn is_sink_decouple(user_specified: &SinkDecouple) -> Result<bool> {
        match user_specified {
            SinkDecouple::Default | SinkDecouple::Enable => Ok(true),
            SinkDecouple::Disable => Err(SinkError::Config(anyhow!(
                "File sink can only be created with sink_decouple enabled. Please run `set sink_decouple = true` first."
            ))),
        }
    }

    async fn validate(&self) -> Result<()> {
        if matches!(self.engine_type, EngineType::Snowflake) {
            risingwave_common::license::Feature::SnowflakeSink
                .check_available()
                .map_err(|e| anyhow::anyhow!(e))?;
        }
        if !self.is_append_only {
            return Err(SinkError::Config(anyhow!(
                "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')`"
            )));
        }

        if self.format_desc.encode != SinkEncode::Parquet
            && self.format_desc.encode != SinkEncode::Json
        {
            return Err(SinkError::Config(anyhow!(
                "File sink only supports `PARQUET` and `JSON` encode at present."
            )));
        }

        match self.op.list(&self.path).await {
            Ok(_) => Ok(()),
            Err(e) => Err(anyhow!(e).into()),

View on GitHub (pinned to 6469eb736d)

Solutions

  1. Add `force_append_only = 'true'` to the ENCODE options, e.g. `FORMAT PLAIN ENCODE JSON(force_append_only = 'true')`
  2. Ensure the input stream is genuinely append-only (use an append-only MV or aggregation without a key)
  3. If updates must be preserved, use a non-file sink (e.g. JDBC/Kafka upsert) instead of a file sink

Example fix

// before
CREATE SINK s FROM mv WITH (connector = 's3', type = 'append-only')
FORMAT PLAIN ENCODE JSON;
// after
CREATE SINK s FROM mv WITH (connector = 's3', type = 'append-only')
FORMAT PLAIN ENCODE JSON(force_append_only = 'true');
Defensive patterns

Strategy: validation

Validate before calling

-- only sink append-only MVs; or force append-only output:
CREATE SINK s FROM mv WITH (connector = 's3', ...)
FORMAT PLAIN ENCODE JSON(force_append_only = 'true');

Try / catch

match err {
    e if e.to_string().contains("only supports append-only") => {
        eprintln!("Add force_append_only = 'true' to ENCODE options, or use an append-only source");
    }
    other => return Err(other),
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Understand the failure class

Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.

Related errors


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