{"record":{"id":"59a3d5fd4d55edc9","repo":"risingwavelabs/risingwave","slug":"spill-path-must-be-relative-but-got","errorCode":null,"errorMessage":"Spill path must be relative, but got {:?}","messagePattern":"Spill path must be relative, but got (.+?)","errorType":"error_code","errorClass":"BatchError","httpStatus":null,"severity":"error","filePath":"src/batch/src/spill/spill_op.rs","lineNumber":62,"sourceCode":"    Disk,\n    /// Only for testing purpose\n    Memory,\n}\n\n/// `SpillOp` is used to manage the spill directory of the spilling executor and it will drop the directory with a RAII style.\npub struct SpillOp {\n    pub op: Operator,\n}\n\nimpl SpillOp {\n    fn batch_spill_root() -> PathBuf {\n        batch_spill_base_dir().join(RW_MANAGED_SPILL_DIR)\n    }\n\n    pub fn create(path: impl AsRef<Path>, spill_backend: SpillBackend) -> Result<SpillOp> {\n        let path = path.as_ref();\n        if !path.is_relative() {\n            bail!(\"Spill path must be relative, but got {:?}\", path);\n        }\n\n        let root = Self::batch_spill_root().join(path);\n\n        let op = match spill_backend {\n            SpillBackend::Disk => {\n                let builder = Fs::default().root(&root.to_string_lossy());\n                Operator::new(builder)?.layer(RetryLayer::default())\n            }\n            SpillBackend::Memory => {\n                let builder = Memory::default().root(&root.to_string_lossy());\n                Operator::new(builder)?.layer(RetryLayer::default())\n            }\n        };\n        Ok(SpillOp { op })\n    }\n\n    pub async fn clean_spill_directory() -> opendal::Result<()> {","sourceCodeStart":44,"sourceCodeEnd":80,"githubUrl":"https://github.com/risingwavelabs/risingwave/blob/6469eb736d691e8e9b8a419a57edd6429ca77417/src/batch/src/spill/spill_op.rs#L44-L80","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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)."],"exampleFix":"// before\nlet op = SpillOp::create(\"/tmp/rw/spill/q-42\", backend)?; // absolute -> bail\n\n// after\nlet op = SpillOp::create(\"q-42\", backend)?; // joined onto managed spill root","handlingStrategy":"validation","validationCode":"let path = path.as_ref();\nassert!(path.is_relative(), \"spill path must be relative, got {:?}\", path);","typeGuard":null,"tryCatchPattern":"match SpillOp::create(rel_path, backend) {\n    Err(e) if e.to_string().starts_with(\"Spill path must be relative\") => {\n        // rebuild a relative path (strip base dir) and retry once\n    }\n    other => other?,\n}","preventionTips":["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."],"tags":["spill","path-validation","filesystem"],"backgroundTag":"invalid-argument-value","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"}