{"record":{"id":"a853020bef3d0d05","repo":"astrid-runtime/astrid","slug":"capsule-target-has-no-parent","errorCode":null,"errorMessage":"capsule target has no parent","messagePattern":"capsule target has no parent","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/astrid-capsule-install/src/storage.rs","lineNumber":463,"sourceCode":"        )\n    })?;\n    let authority = serde_json::to_vec_pretty(&durable_authority)\n        .context(\"serialize installed capsule authority receipt\")?;\n    let registry = store.capsules();\n    let owner = StateOwner::Principal(uid);\n    let id = authority_capsule_id(&authority)?;\n    let expected = registry\n        .get_snapshot(&owner, &id)?\n        .map_or(CapsuleInstallExpectation::Absent, |snapshot| {\n            CapsuleInstallExpectation::Generation(snapshot.generation())\n        });\n    let package = CapsulePackage::new(archive, metadata, authority);\n    let materialization = tempfile::Builder::new()\n        .prefix(\".capsule-materialization-\")\n        .tempdir_in(\n            target_dir\n                .parent()\n                .ok_or_else(|| anyhow::anyhow!(\"capsule target has no parent\"))?,\n        )\n        .context(\"stage verified durable capsule materialization\")?;\n    let staged_target = materialization.path().join(\"package\");\n    materialize_capsule_package(&package, &staged_target)\n        .context(\"stage verified durable capsule package\")?;\n\n    // Storage-backed installs initially assemble a lifecycle workspace that\n    // intentionally omits the WASM component. Replace it with the complete\n    // verified package before publication so an immediately triggered live\n    // reload sees the same bytes that a restart would rematerialize.\n    let previous_target = materialization.path().join(\"previous\");\n    fs::rename(target_dir, &previous_target)\n        .context(\"stage incomplete capsule install cache for replacement\")?;\n    if let Err(error) = fs::rename(&staged_target, target_dir) {\n        let _ = fs::rename(&previous_target, target_dir);\n        return Err(error).context(\"publish verified capsule materialization\");\n    }\n","sourceCodeStart":445,"sourceCodeEnd":481,"githubUrl":"https://github.com/astrid-runtime/astrid/blob/affd8760f44190dbdfbec23403f4c4b642c33112/crates/astrid-capsule-install/src/storage.rs#L445-L481","documentation":"publish_directory_package stages a verified package via a temp materialization directory created inside the PARENT of the target directory (so the rename/stage is same-filesystem). If target_dir has no parent (i.e. it is a filesystem root like '/' or an empty relative path resolved to root), the publish cannot create its staging directory and this error is raised.","triggerScenarios":"Calling publish_directory_package with target_dir = '/' or another path whose parent() returns None — e.g. publishing directly to a mount point root or passing an empty/normalized-to-root target path.","commonSituations":"Misconfigured install target pointing at the filesystem root; a script building the target path with join/normalize logic that strips all components; tests passing Path::new(\"/\").","solutions":["Pass a real capsule target directory that has a parent, e.g. ~/.astrid/capsules/<name> rather than / .","Fix path-building code that normalizes the target down to the root; keep at least one directory component.","Validate the target path before publishing (target.is_absolute() && target.parent().is_some()).","If publishing to a mount, use a subdirectory of the mount instead of the mount root."],"exampleFix":"// before\npublish_directory_package(store, registry, \"/\", /* ... */)?;\n// after\nlet target = PathBuf::from(\"/var/lib/capsules/my-capsule\");\npublish_directory_package(store, registry, &target, /* ... */)?;","handlingStrategy":"validation","validationCode":"fn ensure_publishable_target(target: &std::path::Path) -> anyhow::Result<()> {\n    if target.parent().is_none() {\n        anyhow::bail!(\"target {:?} must have a parent directory\", target);\n    }\n    Ok(())\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Never pass '/' or a bare mount root as the capsule target directory.","Keep at least one directory component in generated target paths.","Assert target.parent().is_some() in tests that exercise publish."],"tags":["filesystem","path","publish"],"backgroundTag":"invalid-argument-value","analyzedSha":"affd8760f44190dbdfbec23403f4c4b642c33112","analyzedAt":"2026-09-09T21:28:12.402Z","contentChangedAt":"2026-09-09T21:28:12.402Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}