{"record":{"id":"142a51edddf82a46","repo":"astrid-runtime/astrid","slug":"unsafe-workspace-selection-error","errorCode":null,"errorMessage":"unsafe workspace selection: {error}","messagePattern":"unsafe workspace selection: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/astrid-kernel/src/lib.rs","lineNumber":1065,"sourceCode":"            runtime_key,\n            session_token,\n            token_path,\n            cli_socket_listener,\n            singleton_lock,\n            #[cfg(not(all(target_arch = \"wasm32\", target_os = \"unknown\")))]\n            layout_origin,\n        } = resources;\n        #[cfg(not(unix))]\n        let _ = token_path;\n\n        home.clear_runtime_principal_scratch().map_err(|error| {\n            std::io::Error::other(format!(\n                \"Failed to clear stale principal runtime scratch: {error}\"\n            ))\n        })?;\n\n        let workspace_selection = workspace_layout.resolve(&workspace_root).map_err(|error| {\n            std::io::Error::new(error.kind(), format!(\"unsafe workspace selection: {error}\"))\n        })?;\n        let workspace_root = workspace_selection.project_root().to_path_buf();\n\n        let event_bus = Arc::new(EventBus::new());\n        let capsules = Arc::new(RwLock::new(CapsuleRegistry::new()));\n\n        // The canonical runtime has no native principal-home authority. Home\n        // and workspace mounts are bound from the durable storage provider;\n        // lifecycle compatibility callers receive an explicit `None` and\n        // must not recreate a host `principal_home` tree.\n        let home_root = None;\n\n        // Bootstrap the capability store (persistent) over the injected KV.\n        // Key rotation invalidates persisted tokens (fail-secure by design).\n        let capabilities = Arc::new(\n            CapabilityStore::with_kv_store(Arc::clone(&kv))\n                .await\n                .map_err(|e| {","sourceCodeStart":1047,"sourceCodeEnd":1083,"githubUrl":"https://github.com/astrid-runtime/astrid/blob/affd8760f44190dbdfbec23403f4c4b642c33112/crates/astrid-kernel/src/lib.rs#L1047-L1083","documentation":"During kernel startup, `workspace_layout.resolve(&workspace_root)` rejected the workspace selection as unsafe (e.g. root/symlink/ownership problems per the layout rules). The kernel re-wraps the rejection with its original ErrorKind and the message prefix `unsafe workspace selection:` because it will only operate inside a safely-resolved project root.","triggerScenarios":"Starting the kernel with a workspace root that `workspace_layout.resolve` deems unsafe — symlinked or redirected workspace roots, disallowed locations, or ownership/permission mismatches on the path.","commonSituations":"Running the tool inside a symlinked project directory (or a symlinked HOME chain); a workspace mounted from another user's directory; CI checkouts with unusual symlink structures; containers mapping volumes with mismatched uids.","solutions":["Use the real, non-symlinked path as the workspace root (resolve the canonical path and cd there).","Fix ownership/permissions so the workspace root belongs to the current user.","Move the workspace to a location the layout rules permit.","Read the inner `{error}` in the message — it names the specific layout rule that was violated."],"exampleFix":"// before\nlet root = PathBuf::from(\"~/projects/link-to-app\"); // symlink\n// after\nlet root = std::fs::canonicalize(\"~/projects/link-to-app\")?; // real path\nlet workspace = workspace_layout.resolve(&root)?;","handlingStrategy":"try-catch","validationCode":"let root = std::fs::canonicalize(&workspace_root)?;\nif !root.is_dir() {\n    return Err(\"workspace root must be a real directory\");\n}\nlet selection = workspace_layout.resolve(&root)?;","typeGuard":"fn safe_workspace_root(p: &std::path::Path) -> std::io::Result<bool> {\n    let real = std::fs::canonicalize(p)?;\n    Ok(real.is_dir() && real == p)\n}","tryCatchPattern":"match workspace_layout.resolve(&workspace_root) {\n    Err(e) => return Err(std::io::Error::new(e.kind(), format!(\"unsafe workspace selection: {e}; use a real, non-symlinked, user-owned directory\"))),\n    Ok(sel) => sel,\n};","preventionTips":["Pass canonical (non-symlinked) workspace paths to the kernel.","Ensure the workspace root is owned by the current user with sane permissions.","Avoid running the tool from symlinked checkout or home paths in CI."],"tags":["workspace","security","symlink","filesystem"],"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-14T11:17:12.474Z"}