risingwavelabs/risingwave · error · BatchError

Spill path must be relative, but got {:?}

Error message

Spill path must be relative, but got {:?}

What it means

SpillOp::create validates that the given spill path is relative before joining it onto the managed batch spill root directory. An absolute path would escape the managed root and break spill file lifecycle/cleanup, so creation fails fast with this bail! message that includes the offending path.

Source

Thrown at src/batch/src/spill/spill_op.rs:62

    Disk,
    /// Only for testing purpose
    Memory,
}

/// `SpillOp` is used to manage the spill directory of the spilling executor and it will drop the directory with a RAII style.
pub struct SpillOp {
    pub op: Operator,
}

impl SpillOp {
    fn batch_spill_root() -> PathBuf {
        batch_spill_base_dir().join(RW_MANAGED_SPILL_DIR)
    }

    pub fn create(path: impl AsRef<Path>, spill_backend: SpillBackend) -> Result<SpillOp> {
        let path = path.as_ref();
        if !path.is_relative() {
            bail!("Spill path must be relative, but got {:?}", path);
        }

        let root = Self::batch_spill_root().join(path);

        let op = match spill_backend {
            SpillBackend::Disk => {
                let builder = Fs::default().root(&root.to_string_lossy());
                Operator::new(builder)?.layer(RetryLayer::default())
            }
            SpillBackend::Memory => {
                let builder = Memory::default().root(&root.to_string_lossy());
                Operator::new(builder)?.layer(RetryLayer::default())
            }
        };
        Ok(SpillOp { op })
    }

    pub async fn clean_spill_directory() -> opendal::Result<()> {

View on GitHub (pinned to 6469eb736d)

Solutions

  1. Pass a relative path (e.g. a query/operator-relative directory name); the absolute root is applied by SpillOp::batch_spill_root internally.
  2. If you have an absolute path from config, strip the configured base directory prefix before calling create.
  3. Audit code that builds the spill path to avoid joining absolute components (use relative_name, not base_dir.join).

Example fix

// before
let op = SpillOp::create("/tmp/rw/spill/q-42", backend)?; // absolute -> bail

// after
let op = SpillOp::create("q-42", backend)?; // joined onto managed spill root
Defensive patterns

Strategy: validation

Validate before calling

let path = path.as_ref();
assert!(path.is_relative(), "spill path must be relative, got {:?}", path);

Try / catch

match SpillOp::create(rel_path, backend) {
    Err(e) if e.to_string().starts_with("Spill path must be relative") => {
        // rebuild a relative path (strip base dir) and retry once
    }
    other => other?,
}

Prevention

When it happens

Trigger: Constructing a SpillOp with a path produced by an absolute base (e.g. PathBuf from an env var or config that is absolute, or joining an absolute component) instead of a relative subdirectory under the spill root.

Common situations: Operators configuring the spill base dir to an absolute path and passing it through as the SpillOp path, code changes that switch from a relative name to a fully-qualified path, tests/tmpdir setups using absolute temp paths.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


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