astrid-runtime/astrid · error · io::Error

InvalidData

InvalidData

Error message

invalid private-file write journal

What it means

When recovering, read_private_file_transaction_journal parses the journal JSON and then validate_private_file_transaction checks its integrity: version must be 1, the transaction id a 32-hex-char UUID, target/staged/rollback names single path components containing the id, had_live consistent with rollback/old_hash presence, and both digests 64 hex chars. Any violation means the journal is corrupt or tampered with, so recovery refuses with InvalidData.

Source

Thrown at crates/astrid-core/src/platform_fs/windows/private_file.rs:300

            .is_some_and(|name| !is_single_path_component(OsStr::new(name)))
        || !journal.staged.contains(&journal.transaction_id)
        || journal
            .rollback
            .as_deref()
            .is_some_and(|name| !name.contains(&journal.transaction_id))
        || journal
            .legacy_displaced
            .as_deref()
            .is_some_and(|name| !name.contains(&journal.transaction_id))
        || journal.had_live != journal.rollback.is_some()
        || journal.had_live != journal.old_hash.is_some()
        || !valid_digest(&journal.new_hash)
        || journal
            .old_hash
            .as_deref()
            .is_some_and(|hash| !valid_digest(hash))
    {
        return Err(io::Error::new(
            io::ErrorKind::InvalidData,
            "invalid private-file write journal",
        ));
    }
    Ok(())
}

pub(super) fn is_single_path_component(value: &OsStr) -> bool {
    let mut components = Path::new(value).components();
    matches!(components.next(), Some(Component::Normal(_))) && components.next().is_none()
}

pub(super) fn finish_private_file_transaction(
    parent: &Path,
    journal: &PrivateFileTransaction,
    guard: &TrustedPathGuard,
) -> io::Result<()> {
    guard

View on GitHub (pinned to affd8760f4)

Solutions

  1. Inspect .astrid-private-write.transaction.json; if it is corrupt and no live data depends on it, remove the journal and its .astrid-private.* companion files so the next operation starts clean (this discards the pending transaction's rollback).
  2. Verify the live target file's contents manually before deleting the journal; if the write actually completed, the data is intact and only the journal is stale.
  3. Check library version consistency: journals written by other versions may fail validation; upgrade/downgrade to the version that wrote the journal to recover it properly.

Example fix

// before: recovery keeps failing with InvalidData on a corrupt journal
// after: manually clear the pending transaction after confirming the target file
if std::fs::read(&state).map(|d| sha256(&d) == expected).unwrap_or(false) {
    std::fs::remove_file(dir.join(".astrid-private-write.transaction.json")).ok();
    atomic_write_private_file(&state, data)?;
}
Defensive patterns

Strategy: try-catch

Try / catch

match result {
    Err(e) if e.kind() == std::io::ErrorKind::InvalidData && e.to_string().contains("invalid private-file write journal") => {
        // journal corrupt: verify live file, remove journal + .astrid-private.* files, retry
    }
    other => other?,
}

Prevention

When it happens

Trigger: The journal file on disk was truncated, hand-edited, corrupted by a crash/partial flush, or written by an incompatible library version; the file passes JSON parsing but fails structural validation.

Common situations: Disk corruption or incomplete flush after power loss; a user manually edited/deleted parts of the journal; downgrading the library so an older/newer journal shape fails the deny_unknown_fields parse or version check.

Understand the failure class

Background: Checksum mismatch errors: "checksum verification failed", "digest mismatch", "expected vs actual checksum" — what they mean and how to fix them — this error's family across 41 libraries.

Related errors


AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09). Data as JSON: /api/errors/d703cd9dea54221e. Report an issue: GitHub.