screenpipe/screenpipe · error
cannot determine parent dir of {:?}
Error message
cannot determine parent dir of {:?} What it means
atomic_write writes pipe files via a temp file plus atomic rename/persist. It needs the target's parent directory to place the temp file; if path.parent() is None (path has no directory component, e.g. a bare relative filename), it throws this error.
Source
Thrown at crates/screenpipe-core/src/pipes/mod.rs:7272
.find("\n---")
.ok_or_else(|| anyhow!("could not find closing --- in front-matter"))?;
let yaml_str = &rest[..end];
let body = rest[end + 4..].trim().to_string();
let config: PipeConfig = serde_yaml::from_str(yaml_str)?;
Ok((config, body))
}
/// Atomic file write: write to a temp file in the same directory, then rename.
/// On Unix, rename is atomic. On Windows, this avoids the partial-write window
/// where a concurrent reader (e.g. the scheduler) sees a truncated file.
fn atomic_write(path: &Path, content: &str) -> Result<()> {
use std::io::Write;
let dir = path
.parent()
.ok_or_else(|| anyhow!("cannot determine parent dir of {:?}", path))?;
let mut tmp = tempfile::NamedTempFile::new_in(dir)?;
tmp.write_all(content.as_bytes())?;
tmp.flush()?;
// persist atomically (rename on Unix, MoveFileEx on Windows)
tmp.persist(path)?;
Ok(())
}
/// Serialize a PipeConfig + body back to pipe.md format.
/// Name is excluded from frontmatter (derived from directory name).
pub fn serialize_pipe(config: &PipeConfig, body: &str) -> Result<String> {
let mut cfg = config.clone();
cfg.name = String::new(); // empty → skip_serializing_if kicks in
// Remove legacy "config" key from extras — old pipe.md files had a nested
// `config: { enabled: true }` block that gets captured by the flattened
// HashMap and re-emitted forever. Also strip any keys that shadow real
// struct fields to prevent duplicates.View on GitHub (pinned to 4ebf712990)
Solutions
- Join the filename onto an absolute directory before writing: PathBuf::from(pipe_dir).join("pipe.md").
- Call canonicalize() (or std::env::current_dir().join(...)) on relative paths before passing them.
- Pass paths derived from the pipe's install directory rather than bare names.
Example fix
// before
atomic_write(Path::new("pipe.md"), content)?;
// after
let path = pipe_dir.join("pipe.md");
atomic_write(&path, content)?; Defensive patterns
Strategy: validation
Validate before calling
const path = require('path');
const p = path.resolve(targetPath); // ensures a directory component exists
if (path.dirname(p) === p) throw new Error('path has no parent directory: ' + p); Type guard
fn has_parent(path: &Path) -> bool {
path.parent().is_some()
} Try / catch
match atomic_write(&path, content) {
Err(e) if e.to_string().starts_with("cannot determine parent dir") => {
eprintln!("pass a path with a directory component, e.g. dir.join(\"pipe.md\")");
}
other => other?,
} Prevention
- Always build write targets as directory.join(filename), never bare filenames.
- Canonicalize user-supplied paths before persistence.
- Assert Path::parent().is_some() in debug builds for write helpers.
When it happens
Trigger: Calling a pipe-writing operation that ends in atomic_write with a Path like "pipe.md" (no directory prefix) or a root-less path constructed from components only.
Common situations: Programmatically building paths as Path::new("file.md") instead of joining a directory; passing a filename from config without resolving it against the pipe directory.
Related errors
- could not determine Pi session directory
- extracted frame has no filename
- Failed to create valid path
- createProvider: a non-empty model string is required
- path does not exist: {}
AI-assisted analysis of screenpipe/screenpipe@4ebf712990 (2026-09-01).
Data as JSON: /api/errors/c18b8fd47fc45ca6.
Report an issue: GitHub.