astrid-runtime/astrid · error

legacy source entry is not owned by the current user: {}

Error message

legacy source entry is not owned by the current user: {}

What it means

Under SourceAccess::OwnerControlled, every entry in the legacy source must be owned by the current user (metadata.uid() == getuid()). If any file or directory is owned by another uid, the migration refuses with PermissionDenied to prevent importing files whose owner-controlled semantics you cannot actually enforce.

Source

Thrown at crates/astrid-kernel/src/legacy_migration_barrier/host_fs.rs:777

    match access {
        SourceAccess::Private => validate_private_entry(path, metadata),
        SourceAccess::OwnerControlled => validate_owner_controlled_entry(path, metadata),
    }
}

fn validate_owner_controlled_entry(path: &Path, metadata: &fs::Metadata) -> io::Result<()> {
    if !metadata.is_dir() && !metadata.is_file() {
        return Err(io::Error::new(
            io::ErrorKind::InvalidData,
            format!("legacy source contains a special entry: {}", path.display()),
        ));
    }
    #[cfg(unix)]
    {
        use std::os::unix::fs::MetadataExt as _;

        if metadata.uid() != nix::unistd::getuid().as_raw() {
            return Err(io::Error::new(
                io::ErrorKind::PermissionDenied,
                format!(
                    "legacy source entry is not owned by the current user: {}",
                    path.display()
                ),
            ));
        }
        if metadata.mode() & 0o022 != 0 {
            return Err(io::Error::new(
                io::ErrorKind::PermissionDenied,
                format!(
                    "legacy source entry is group/world writable: {}",
                    path.display()
                ),
            ));
        }
        astrid_core::platform_fs::validate_no_extended_acl(path)?;
        Ok(())

View on GitHub (pinned to affd8760f4)

Solutions

  1. chown the tree to the current user: `sudo chown -R $(id -u):$(id -g) <source>`
  2. Copy the files with your own user (cp/rsync as yourself) so the copies are owned by you
  3. Use SourceAccess::Private if a different ownership/permission profile is acceptable
  4. Run the migration as the user who owns the data

Example fix

// before: entry owned by root, app runs as 'alice'
// $ sudo tar -xzf backup.tgz -C /home/alice/data
let result = snapshot_path_with_access(path, SourceAccess::OwnerControlled);
// Err: legacy source entry is not owned by the current user: ...

// after: take ownership before migrating
// $ sudo chown -R alice:alice /home/alice/data
let result = snapshot_path_with_access(path, SourceAccess::OwnerControlled);
Defensive patterns

Strategy: validation

Validate before calling

#[cfg(unix)]
fn assert_owned_by_current_user(source: &std::path::Path) -> std::io::Result<()> {
    use std::os::unix::fs::MetadataExt;
    let uid = nix::unistd::getuid().as_raw();
    let mut stack = vec![source.to_path_buf()];
    while let Some(dir) = stack.pop() {
        for entry in std::fs::read_dir(&dir)? {
            let path = entry?.path();
            let md = std::fs::symlink_metadata(&path)?;
            if md.uid() != uid {
                return Err(std::io::Error::new(
                    std::io::ErrorKind::PermissionDenied,
                    format!("not owned by current user: {}", path.display()),
                ));
            }
            if md.is_dir() {
                stack.push(path);
            }
        }
    }
    Ok(())
}

Try / catch

match snapshot_path_with_access(source, SourceAccess::OwnerControlled) {
    Err(e) if e.kind() == std::io::ErrorKind::PermissionDenied
        && e.to_string().contains("not owned by the current user") =>
    {
        eprintln!("fix with: sudo chown -R $(id -u):$(id -g) <source>");
    }
    other => other?,
}

Prevention

When it happens

Trigger: Calling the migration API with SourceAccess::OwnerControlled on a tree containing any entry owned by a different user — e.g. files created by root (via sudo), by another account, or by a container process with a different uid; also files extracted from archives as root.

Common situations: Running the app as a non-root user over data previously written by root; unpacking a tarball with sudo into the source directory; shared directories where a teammate or service account owns some files; NFS rootsquash mapping uids.

Understand the failure class

Background: Permission denied / not authorized / 403 Forbidden: access-control rejections when the caller lacks the required role, grant, or ownership — this error's family across 18 libraries.

Related errors


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