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.

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.

Appendix: 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)