libnyanpasu/clash-nyanpasu · error
materialization journal is not a regular file: {}
Error message
materialization journal is not a regular file: {} What it means
During profile materialization, `read_journal` loads a journal YAML file (named `<operation_id>.yaml`) from the materialization root's staging/backup/cleanup directory to verify and resume an operation. Before parsing it, the code uses `symlink_metadata` to check the entry is a plain regular file; if it is a symlink/reparse point or a directory (or other non-file entry), this error is thrown. This guards against tampered or corrupted private storage where a journal path has been replaced by something other than the expected file.
Source
Thrown at backend/tauri/src/service/profile_file.rs:612
}
Ok(())
}
fn write_journal_new(path: &Path, journal: &MaterializationJournal) -> anyhow::Result<()> {
let content =
serde_yaml::to_string(journal).context("serialize materialization journal")?;
AtomicFile::new(path, OverwriteBehavior::DisallowOverwrite)
.write(|file| file.write_all(content.as_bytes()))
.with_context(|| format!("write materialization journal {}", path.display()))?;
set_private_file_permissions(path)?;
sync_directory(path.parent().expect("journal has parent"))
}
fn read_journal(path: &Path, operation_id: &str) -> anyhow::Result<MaterializationJournal> {
let metadata = std::fs::symlink_metadata(path)
.with_context(|| format!("inspect materialization journal {}", path.display()))?;
if is_symlink_or_reparse(&metadata) || !metadata.is_file() {
bail!(
"materialization journal is not a regular file: {}",
path.display()
);
}
let content = std::fs::read_to_string(path)
.with_context(|| format!("read materialization journal {}", path.display()))?;
let journal: MaterializationJournal = serde_yaml::from_str(&content)
.with_context(|| format!("parse materialization journal {}", path.display()))?;
if journal.operation_id != operation_id || !valid_operation_id(&journal.operation_id) {
bail!("materialization journal operation id mismatch");
}
if journal
.managed_path
.as_path()
.components()
.any(|component| is_materialization_root_name(component.as_os_str()))
{
bail!("materialization journal targets reserved private storage");View on GitHub (pinned to f7dbce2997)
Solutions
- Delete the offending entry at the reported path so the materialization code can treat the operation as absent/redo it
- Check what the path is (ls -la / dir /R) to confirm it is a symlink or directory before deleting
- Exclude the app's private materialization root from symlink-creating dotfile managers and cloud sync tools
- If operations repeatedly fail, wipe the materialization staging/backup/cleanup directories (they are transient) and re-run the profile materialization
Example fix
// before: journal path replaced by a symlink ~/.local/share/nyanpasu/materialization/staging/journals/abc123.yaml -> /etc/passwd // after: remove the non-regular entry and let the app recreate it rm ~/.local/share/nyanpasu/materialization/staging/journals/abc123.yaml
Defensive patterns
Strategy: validation
Validate before calling
use std::fs::symlink_metadata;
fn journal_is_regular(path: &std::path::Path) -> bool {
match symlink_metadata(path) {
Ok(md) => md.is_file() && !md.file_type().is_symlink(),
Err(_) => false,
}
} Type guard
fn is_regular_file_md(md: &std::fs::Metadata) -> bool {
md.is_file() && !md.file_type().is_symlink()
} Try / catch
match read_journal(&path, &op_id) {
Err(e) if e.to_string().contains("not a regular file") => {
let _ = std::fs::remove_file(&path); // drop tampered entry, re-materialize
}
Err(e) => return Err(e),
Ok(j) => use_journal(j),
} Prevention
- Never symlink files inside the app's private materialization root
- Exclude the materialization directory from cloud-sync and dotfile managers
- Do not create directories at journal paths
- Treat the materialization root as app-owned; recreate it rather than repairing by hand
When it happens
Trigger: `read_journal` is called with a journal path whose `symlink_metadata` reports a symlink, Windows reparse point, directory, FIFO, or other non-regular-file entry at `<root>/{staging,backup,cleanup}/.../<operation_id>.yaml`. E.g. a user or tool replaced the journal file with a symlink, or a directory was created where the journal should be.
Common situations: Manual tampering with the app's private materialization directory; restore/sync tools (dotfile managers, cloud sync) that convert files into symlinks; a crashed or buggy older version leaving a directory at the journal path; malware or an overzealous cleanup script.
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
- runtime candidate directory is a symlink or reparse point: {
- profile directory is a symlink, reparse point, or non-direct
- private materialization artifact is not a regular file: {}
- refusing to write through unexpected symlink or reparse poin
- materialization journal targets reserved private storage
AI-assisted analysis of libnyanpasu/clash-nyanpasu@f7dbce2997 (2026-09-08).
Data as JSON: /api/errors/8a553e0b2367ff5a.
Report an issue: GitHub.