libnyanpasu/clash-nyanpasu · error

ready cleanup journal is not a regular file

Error message

ready cleanup journal is not a regular file

What it means

Identical guard to the Pending phase, applied to the Ready cleanup journal: if the Ready phase path exists as a symlink/reparse point or non-regular file, locate_cleanup refuses to continue. Recovery requires both phase slots to be either absent or plain regular files so that payloads can be compared safely.

Source

Thrown at backend/tauri/src/service/profile_file.rs:1348

    ) -> anyhow::Result<Option<(CleanupPhase, MaterializationJournal)>> {
        if !valid_operation_id(operation_id) {
            bail!("invalid profile cleanup operation id");
        }
        let pending_path = Self::cleanup_path(root, CleanupPhase::Pending, operation_id);
        let ready_path = Self::cleanup_path(root, CleanupPhase::Ready, operation_id);
        let pending = match std::fs::symlink_metadata(&pending_path) {
            Ok(metadata) if !is_symlink_or_reparse(&metadata) && metadata.is_file() => {
                Some(Self::read_journal(&pending_path, operation_id)?)
            }
            Ok(_) => bail!("pending cleanup journal is not a regular file"),
            Err(error) if error.kind() == std::io::ErrorKind::NotFound => None,
            Err(error) => return Err(error).context("inspect pending cleanup journal"),
        };
        let ready = match std::fs::symlink_metadata(&ready_path) {
            Ok(metadata) if !is_symlink_or_reparse(&metadata) && metadata.is_file() => {
                Some(Self::read_journal(&ready_path, operation_id)?)
            }
            Ok(_) => bail!("ready cleanup journal is not a regular file"),
            Err(error) if error.kind() == std::io::ErrorKind::NotFound => None,
            Err(error) => return Err(error).context("inspect ready cleanup journal"),
        };
        match (pending, ready) {
            (None, None) => Ok(None),
            (Some(journal), None) => Ok(Some((CleanupPhase::Pending, journal))),
            (None, Some(journal)) => Ok(Some((CleanupPhase::Ready, journal))),
            (Some(pending), Some(ready)) if pending == ready => {
                Self::remove_private_regular(&pending_path)?;
                Ok(Some((CleanupPhase::Ready, ready)))
            }
            (Some(_), Some(_)) => bail!("cleanup journals have conflicting payloads"),
        }
    }

    fn has_materialization_journal(root: &Path, operation_id: &str) -> bool {
        JournalLocation::ALL.iter().any(|location| {
            std::fs::symlink_metadata(Self::journal_path(root, *location, operation_id)).is_ok()

View on GitHub (pinned to f7dbce2997)

Solutions

  1. Delete or replace the non-file artifact at the reported Ready path and rerun the lookup.
  2. Make the journal directory local-only (exclude from cloud sync/space-saver placeholders).
  3. Clear the entire cleanup operation state for that operation_id and restart the cleanup from scratch.
  4. Audit what wrote to the journal directory if this recurs — external tooling is interfering.

Example fix

// before
let state = locate_cleanup(root, &id)?; // bails on Ready artifact
// after
let r = cleanup_path(root, CleanupPhase::Ready, &id);
let meta = std::fs::symlink_metadata(&r)?;
if meta.is_symlink() || !meta.is_file() {
    std::fs::remove_file(&r)?;
}
let state = locate_cleanup(root, &id)?;
Defensive patterns

Strategy: validation

Validate before calling

fn ready_journal_clean(root: &Path, id: &str) -> Result<bool, std::io::Error> {
    match std::fs::symlink_metadata(cleanup_path(root, CleanupPhase::Ready, id)) {
        Err(e) if e.kind() == std::io::ErrorKind::NotFound => Ok(true),
        Err(e) => Err(e),
        Ok(m) => Ok(!m.is_symlink() && m.is_file()),
    }
}

Type guard

fn is_plain_file(m: &std::fs::Metadata) -> bool {
    !m.is_symlink() && m.is_file()
}

Try / catch

match locate_cleanup(root, id) {
    Err(e) if e.to_string().contains("ready cleanup journal is not a regular file") => {
        // clear the non-file Ready artifact, then rerun the lookup
    }
    other => other,
}

Prevention

When it happens

Trigger: Cleanup lookup/recovery for an operation whose Ready cleanup journal path (cleanup_path(root, CleanupPhase::Ready, operation_id)) exists as a symlink, directory, or Windows reparse point — typically sync-client placeholders or manual/test tampering.

Common situations: Crash-recovery run on a machine where OneDrive 'files on demand' turned the Ready journal into a cloud placeholder; CI or scripts leaving directories in the journal folder; security tools substituting files.

Understand the failure class

Background: "is not a compatible type" / "cannot merge" errors: when a value's type doesn't match what the library requires — this error's family across 65 libraries.

Related errors


AI-assisted analysis of libnyanpasu/clash-nyanpasu@f7dbce2997 (2026-09-08). Data as JSON: /api/errors/9fe3b0543c41f500. Report an issue: GitHub.