unicity-aos/aos-ce · error · io::Error

standalone runtime has no existing system lock; refusing an…

Error message

standalone runtime has no existing system lock; refusing an unlocked migration

What it means

`SourceRuntimeLock::acquire` opens `<source>/run/system.lock` before migrating a standalone runtime. If the symlink metadata lookup fails with `NotFound`, the library refuses to proceed: migrating a source runtime that has no system lock would not be guarded against an active runtime, so it raises `InvalidInput`. The lock must already exist to prove the source is a real standalone runtime.

Solutions

  1. Verify the source path is the standalone runtime root containing `run/system.lock` (e.g. `ls <source>/run/system.lock`) and correct the path.
  2. If the runtime should be running/initialized, start it once so the runtime creates `run/system.lock`, then retry the migration.
  3. If the lock was deleted, restore the runtime layout (or reinstall) rather than hand-creating an empty lock, since the file must be a real regular file matching metadata.
  4. Ensure you are migrating the right runtime kind — a non-standalone runtime may legitimately lack this lock and is not a valid migration source.

Example fix

// before
migrate_runtime(Path::new("/opt/wrong-runtime"), ...);

// after
let source = Path::new("/opt/unicity-runtime");
assert!(source.join("run/system.lock").exists(), "missing run/system.lock");
migrate_runtime(source, ...);
Defensive patterns

Strategy: validation

Validate before calling

let lock = source.join("run/system.lock");
if !lock.symlink_metadata().map(|m| m.is_file()).unwrap_or(false) {
    return Err(anyhow!("{} is not a standalone runtime (missing run/system.lock)", source.display()));
}

Try / catch

match err.downcast_ref::<io::Error>() {
    Some(e) if e.kind() == io::ErrorKind::InvalidInput && e.to_string().contains("no existing system lock") => /* point at correct runtime root */,
    _ => return Err(err),
}

Prevention

When it happens

Trigger: Calling the migration entry point (which internally calls `SourceRuntimeLock::acquire`) against a source path whose `run/system.lock` file does not exist — e.g. wrong source path, never-started runtime, or a runtime layout where `run/` is missing.

Common situations: Pointing the migration at a stale or half-deleted installation, migrating a containerized/systemd runtime that stores its lock elsewhere, or a typo in the source directory passed on the command line.

Understand the failure class

Background: "File not found" and ENOENT errors: why libraries can't find a file that should exist — this error's family across 50 libraries.

Related errors


AI-assisted analysis of unicity-aos/aos-ce@f6f22024fb (2026-09-13). Data as JSON: /api/errors/4d217a500904e647. Report an issue: GitHub.

Appendix: source

Thrown at crates/unicity-aos-bootstrap/src/migration.rs:157

        let value = String::deserialize(deserializer)?;
        Self::parse(&value).map_err(D::Error::custom)
    }
}

struct MigrationLock {
    _file: File,
}

struct SourceRuntimeLock {
    _file: File,
}

impl SourceRuntimeLock {
    fn acquire(source: &Path) -> io::Result<Self> {
        let path = source.join("run/system.lock");
        let path_metadata = fs::symlink_metadata(&path).map_err(|error| {
            if error.kind() == io::ErrorKind::NotFound {
                io::Error::new(
                    io::ErrorKind::InvalidInput,
                    "standalone runtime has no existing system lock; refusing an unlocked migration",
                )
            } else {
                error
            }
        })?;
        if path_metadata.file_type().is_symlink() || !path_metadata.is_file() {
            return invalid("standalone runtime system lock must be a real regular file");
        }
        let file = OpenOptions::new().read(true).write(true).open(&path)?;
        let file_metadata = file.metadata()?;
        if !file_metadata.is_file() || !same_file(&path_metadata, &file_metadata) {
            return invalid("standalone runtime system lock changed while it was opened");
        }
        file.try_lock_exclusive().map_err(|error| {
            if error.kind() == io::ErrorKind::WouldBlock {
                io::Error::new(

View on GitHub (pinned to f6f22024fb)