libnyanpasu/clash-nyanpasu · error
journal source is not a regular file: {}
Error message
journal source is not a regular file: {} What it means
rename_journal_same_filesystem inspects the journal source with symlink_metadata and rejects any node that is a symlink/reparse point or not a regular file before renaming it. Like the journal-read check, this defends the atomic rename protocol against following attacker-controlled links or renaming directories/special nodes into journal positions.
Source
Thrown at backend/tauri/src/service/profile_file.rs:1179
if duplicate_journal != journal {
bail!(
"materialization operation has conflicting journal payloads in {duplicate_location:?}"
);
}
Self::remove_private_regular(&duplicate_path)?;
}
Ok(Some((location, journal)))
}
/// The journal locations share one private root, so Unix `rename` is an
/// atomic same-filesystem phase transition. Do not use `move_atomic`: its
/// hard-link/unlink fallback can leave duplicate phase artifacts.
#[allow(dead_code)]
fn rename_journal_same_filesystem(source: &Path, destination: &Path) -> anyhow::Result<()> {
let metadata = std::fs::symlink_metadata(source)
.with_context(|| format!("inspect journal source {}", source.display()))?;
if is_symlink_or_reparse(&metadata) || !metadata.is_file() {
bail!("journal source is not a regular file: {}", source.display());
}
std::fs::rename(source, destination).with_context(|| {
format!(
"atomically rename journal {} -> {}",
source.display(),
destination.display()
)
})?;
sync_directory(source.parent().expect("journal source has parent"))?;
if source.parent() != destination.parent() {
sync_directory(
destination
.parent()
.expect("journal destination has parent"),
)?;
}
Ok(())
}View on GitHub (pinned to f7dbce2997)
Solutions
- Verify the source path passed in is the actual journal file path, not a parent directory or link.
- Remove the non-regular node (after confirming it is not legitimate) and recreate the journal via the normal write path.
- Restrict staging-root permissions so only the app user can create nodes there.
- Note: this function is currently #[allow(dead_code)]; if you hit this in tests or new call sites, ensure callers validate node type before invoking.
Example fix
// before rename_journal_same_filesystem(&dir_path, &dest)?; // dir passed, not file // after let meta = std::fs::symlink_metadata(&journal_file)?; assert!(meta.is_file(), "journal source must be a regular file"); rename_journal_same_filesystem(&journal_file, &dest)?;
Defensive patterns
Strategy: validation
Validate before calling
let meta = std::fs::symlink_metadata(&source)?;
if meta.file_type().is_symlink() || !meta.is_file() {
bail!("refusing to rename non-regular journal source: {}", source.display());
} Type guard
fn is_regular_file_node(p: &Path) -> bool {
std::fs::symlink_metadata(p).map(|m| m.is_file()).unwrap_or(false)
} Try / catch
match rename_journal_same_filesystem(&src, &dst) {
Err(e) if e.to_string().contains("not a regular file") => {
// inspect the node; remove if it's not legitimate, then recreate journal
bail!("journal source invalid, operation must be redone")
}
r => r,
} Prevention
- Pass exact journal file paths, never directories or links.
- Keep the journal directory user-private to block symlink planting.
- Recreate journals only through the library's write path.
- If adding call sites to this (currently dead-code) helper, validate node type upstream first.
When it happens
Trigger: rename_journal_same_filesystem called with a source path that is a symlink, directory, or device node — e.g. a planted symlink at the journal location in a shared directory, or a caller passing a wrong path that happens to exist as a directory.
Common situations: Untrusted processes writing into a world-writable staging root; refactored callers passing directory paths instead of journal file paths; filesystems materializing links as other node types.
Understand the failure class
Background: Path traversal blocked: "path escapes the workspace" and "outside site root" errors when a path will not stay inside its allowed directory — this error's family across 26 libraries.
Related errors
- runtime candidate directory is a symlink or reparse point: {
- profile directory is a symlink, reparse point, or non-direct
- materialization journal is not a regular file: {}
- materialization journal targets reserved private storage
- private materialization artifact is not a regular file: {}
AI-assisted analysis of libnyanpasu/clash-nyanpasu@f7dbce2997 (2026-09-08).
Data as JSON: /api/errors/57154bec3f23c4cb.
Report an issue: GitHub.