{"record":{"id":"17e13036c6b52d40","repo":"xai-org/grok-build","slug":"local-workspace-cwd-must-be-an-existing-directory","errorCode":null,"errorMessage":"local workspace cwd must be an existing directory: {}","messagePattern":"local workspace cwd must be an existing directory: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/codegen/xai-grok-pager/src/app/session_startup.rs","lineNumber":517,"sourceCode":"            .join(path)\n    };\n    let canon = abs.canonicalize().map_err(|e| {\n        anyhow::anyhow!(\n            \"local workspace cwd must exist and be canonicalizable: {}: {e}\",\n            abs.display()\n        )\n    })?;\n    if !canon.is_dir() {\n        anyhow::bail!(\n            \"local workspace cwd must be an existing directory: {}\",\n            canon.display()\n        );\n    }\n    if env_truthy(GROK_CHAT_LOCAL_WORKSPACE_ALLOW_HOME_ENV) {\n        return Ok(canon);\n    }\n    if canon == std::path::Path::new(\"/\") {\n        anyhow::bail!(\"{LOCAL_WORKSPACE_HOME_DENIED}\");\n    }\n    if let Some(home_path) = xai_dirs::home_dir() {\n        let home_canon = home_path.canonicalize().unwrap_or(home_path);\n        if canon == home_canon {\n            anyhow::bail!(\"{LOCAL_WORKSPACE_HOME_DENIED}\");\n        }\n    }\n    Ok(canon)\n}\n/// Banner and first-run confirm for the local-workspace own and attach modes.\n///\n/// Skip confirm only with `GROK_CHAT_LOCAL_WORKSPACE_ACK=1` or a prior ack file.\n/// Non-TTY without ACK refuses (fail closed).\n#[cfg(feature = \"local-workspace\")]\npub fn emit_local_workspace_startup_ux(cfg: &LocalWorkspaceConfig) -> anyhow::Result<()> {\n    use std::io::IsTerminal;\n    emit_local_workspace_startup_ux_with(cfg, std::io::stdin().is_terminal())\n}","sourceCodeStart":499,"sourceCodeEnd":535,"githubUrl":"https://github.com/xai-org/grok-build/blob/bc7f02eddd3d84085849dc19ed216f11c23b0571/crates/codegen/xai-grok-pager/src/app/session_startup.rs#L499-L535","documentation":"After canonicalization, `validate_local_workspace_cwd` requires the path to be an existing directory. If the canonical path is not a directory (e.g. it is a file, socket, or fifo), it bails with `local workspace cwd must be an existing directory: <path>` (session_startup.rs:508-511).","triggerScenarios":"Pointing `--local-workspace` cwd, its CLI argument, or GROK_CHAT_LOCAL_WORKSPACE_CWD at a regular file that exists and canonicalizes fine but is not a directory.","commonSituations":"Passing a config file or archive path instead of a folder; tab-completion grabbing a file; cwd env var pointing at a file created by a previous tool run.","solutions":["Point the cwd at a directory, not a file (`mkdir` one if needed)","Correct the GROK_CHAT_LOCAL_WORKSPACE_CWD value","Remove/rename the file if the intended path was supposed to be a directory"],"exampleFix":"// before\nexport GROK_CHAT_LOCAL_WORKSPACE_CWD=/tmp/notes.txt\n// after\nmkdir -p /tmp/notes\nexport GROK_CHAT_LOCAL_WORKSPACE_CWD=/tmp/notes","handlingStrategy":"validation","validationCode":"fn ensure_is_dir(p: &std::path::Path) -> Result<(), String> {\n    if p.is_file() { return Err(format!(\"{p:?} is a file; pass a directory\")); }\n    Ok(())\n}","typeGuard":"fn is_existing_dir(p: &std::path::Path) -> bool { p.canonicalize().map(|c| c.is_dir()).unwrap_or(false) }","tryCatchPattern":"match validate_local_workspace_cwd(&cwd) {\n    Err(e) if e.to_string().contains(\"existing directory\") => {\n        eprintln!(\"point the workspace at a directory, not a file: {e}\")\n    }\n    other => other?,\n}","preventionTips":["Tab-complete directories with a trailing slash in scripts","Never pass file paths (configs, archives) as workspace cwd","Double-check GROK_CHAT_LOCAL_WORKSPACE_CWD targets a folder"],"tags":["filesystem","path-validation","local-workspace"],"backgroundTag":"path-not-a-directory","analyzedSha":"bc7f02eddd3d84085849dc19ed216f11c23b0571","analyzedAt":"2026-08-31T04:59:42.031Z","schemaVersion":2},"datasetVersion":"2026-08-31T09:17:48.483Z"}