astrid-runtime/astrid · error
Workspace capsule directory changed during manifest lookup:
Error message
Workspace capsule directory changed during manifest lookup: {e} What it means
Secret capsule manifests are resolved by verifying the workspace `capsules` tree multiple times (TOCTOU protection). When a capsule manifest does not exist in the workspace, the code re-verifies the tree before returning `Ok(None)`; if that second verification fails, the capsule directory changed (or was found unsafe) during the lookup and this error is thrown instead of silently returning "not found".
Source
Thrown at crates/astrid-cli/src/commands/secret.rs:133
.join(capsule.as_str())
.join("Capsule.toml");
if principal_manifest.exists() {
return read_capsule_manifest(&principal_manifest).map(Some);
}
let Some(workspace_root) = workspace_root else {
return Ok(None);
};
let workspace = workspace_layout
.resolve(workspace_root)
.context("Failed to resolve the selected workspace")?;
let capsules_dir = workspace
.verify_tree("capsules")
.context("Workspace capsule directory is unsafe")?;
let workspace_manifest = capsules_dir.join(capsule.as_str()).join("Capsule.toml");
if !workspace_manifest.exists() {
workspace.verify_tree("capsules").map_err(|e| {
anyhow::anyhow!("Workspace capsule directory changed during manifest lookup: {e}")
})?;
return Ok(None);
}
let manifest = read_capsule_manifest(&workspace_manifest)?;
workspace
.verify_tree("capsules")
.context("Workspace capsule directory changed while reading its manifest")?;
Ok(Some(manifest))
}
#[cfg(test)]
fn read_capsule_manifest(manifest_path: &Path) -> Result<CapsuleManifest> {
let contents = fs::read_to_string(manifest_path)
.with_context(|| format!("Failed to read {}", manifest_path.display()))?;
let manifest: CapsuleManifest = toml::from_str(&contents)
.with_context(|| format!("Failed to parse {}", manifest_path.display()))?;
Ok(manifest)
}View on GitHub (pinned to affd8760f4)
Solutions
- Retry the lookup once the workspace is quiescent; the change may be benign concurrent activity.
- Inspect the `capsules` directory for unexpected changes (symlinks, new files, permission changes) that violate the tree verification.
- Stop concurrent writers (sync clients, build scripts) or exclude the workspace from live syncing during secret operations.
- Treat repeated occurrences as a security signal — verify_tree failing twice suggests an unsafe or redirected capsule tree; restore it from a trusted state.
Example fix
// before
let manifest = load_capsule_manifest(&capsule)?; // may fail on concurrent change
// after
let manifest = match load_capsule_manifest(&capsule) {
Ok(m) => m,
Err(e) if e.to_string().contains("changed during manifest lookup") => {
eprintln!("workspace changed; retrying");
load_capsule_manifest(&capsule)?
}
Err(e) => return Err(e),
}; Defensive patterns
Strategy: retry
Validate before calling
// Pre-check capsule tree stability before lookup let before = snapshot_capsules_dir(&workspace)?; let manifest = load_capsule_manifest(&capsule)?; let after = snapshot_capsules_dir(&workspace)?; assert_eq!(before, after, "capsules tree changed during lookup");
Type guard
fn capsules_tree_stable(workspace: &Workspace) -> bool {
workspace.verify_tree("capsules").is_ok()
} Try / catch
match load_capsule_manifest(&capsule) {
Ok(m) => m,
Err(e) if e.to_string().contains("changed during manifest lookup") => {
eprintln!("workspace mutated concurrently; retrying");
load_capsule_manifest(&capsule)?
}
Err(e) => return Err(e),
} Prevention
- Pause file sync tools and build scripts during secret lookups
- Do not add symlinks or redirects into the capsules directory
- Retry once on transient TOCTOU failures
- Treat repeated failures as a possible tampering signal and audit the tree
When it happens
Trigger: Calling secret/capsule manifest lookup (e.g. `load_capsule_manifest_from_home_in_workspace` via `load_capsule_manifest_*`) while the workspace `capsules` directory is modified concurrently — files added/removed, symlink redirects introduced, or permission/structure changes that make `verify_tree("capsules")` fail on the second pass.
Common situations: Another process (editor, sync tool, build script) writing to the capsules directory during a secret lookup; a symlink or redirected workspace capsule being swapped in mid-lookup; an attacker-manipulated or corrupted capsules tree; tests that mutate the workspace concurrently.
Understand the failure class
Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.
Related errors
- workspace selection changed after validation
- legacy capsule {id} changed before retirement
- legacy capsule {id} metadata changed before retirement
- layout migration source changed type: {}
- PermissionDenied
AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09).
Data as JSON: /api/errors/44b1fe2fbc488567.
Report an issue: GitHub.