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
- Pass a relative path (e.g. a query/operator-relative directory name); the absolute root is applied by SpillOp::batch_spill_root internally.
- If you have an absolute path from config, strip the configured base directory prefix before calling create.
- 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
- Always pass relative directory names to SpillOp::create; the root is applied internally.
- Never join absolute paths (env vars, tmpdir handles) into the spill path argument.
- Strip configured base-dir prefixes before delegating to SpillOp.
- Add unit tests covering path construction for spill directories.
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
- Failed to spill out to disk
- I/O error: {0}
- read ca file error: {0}
- config deserialization error: {e}
- `{}` must be {}, or {}
AI-assisted analysis of risingwavelabs/risingwave@6469eb736d (2026-09-11).
Data as JSON: /api/errors/59a3d5fd4d55edc9.
Report an issue: GitHub.