Hmbown/CodeWhale · error · io::Error
LSP file is outside the workspace
Error message
LSP file is outside the workspace
What it means
The LSP workspace file reader confines every requested path to the workspace root. It tries to strip the workspace root (or its canonicalized form) from the requested absolute path; if the path is not under either, it raises PermissionDenied with this message. This is a path-traversal guard preventing LSP document requests from reading files outside the workspace.
Solutions
- Ensure the requested path resolves under the workspace root; use path joining relative to root instead of ../ escapes.
- Canonicalize your path first and verify it starts with the workspace root before calling.
- If the file genuinely lives elsewhere, add that directory to the workspace or open a separate workspace rooted there.
Example fix
// before
read_workspace_text("/other/repo/src/main.rs")
// after
let p = root.join("src/main.rs").canonicalize()?; // inside workspace
read_workspace_text(&p) Defensive patterns
Strategy: validation
Validate before calling
let root = root.canonicalize()?;
let p = requested.canonicalize()?;
if !p.starts_with(&root) { eprintln!("refusing: outside workspace"); } Type guard
fn in_workspace(root: &Path, p: &Path) -> bool {
p.canonicalize().map(|c| c.starts_with(root)).unwrap_or(false)
} Try / catch
match read_workspace_text(p) {
Err(e) if e.kind() == std::io::ErrorKind::PermissionDenied => eprintln!("outside workspace: {p:?}"),
Err(e) => return Err(e),
Ok(text) => use_text(text),
} Prevention
- Build LSP paths by joining onto the workspace root, never with ../ segments.
- Canonicalize before passing absolute paths.
- Be aware symlinks pointing out of the workspace will be rejected.
When it happens
Trigger: Calling read_workspace_text (directly or via diagnostics_for, diagnostics_for_custom, intelligence_at_revision, semantic_locations, diagnostics_for_paths) with an absolute path that is not under the workspace root or its canonical root.
Common situations: Passing a path containing ../ segments that escape the workspace; requesting a file from a sibling project; the LSP client sending a document URI that was resolved through a symlink outside the root.
Understand the failure class
Background: Path traversal blocked: "path escapes the workspace" and "outside site root" errors when a path will not stay inside its allowed directory — this error's family across 26 libraries.
Related errors
- Codewhale-owned OAuth path escaped the credentials directory
- Codewhale-owned OAuth path has an invalid basename
- external credential path contains reparse point
- external credential path escapes its absolute root
- external credential path must be lexically normalized
AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22).
Data as JSON: /api/errors/4a26216331e626c1.
Report an issue: GitHub.
Appendix: source
Thrown at crates/tui/src/lsp/mod.rs:228
pub fn config(&self) -> &LspConfig {
&self.config
}
async fn read_workspace_text(&self, file: &Path) -> std::io::Result<String> {
let root = self.workspace.clone();
let requested = file.to_path_buf();
tokio::task::spawn_blocking(move || {
use std::io::Read;
// The caller may already have resolved the configured workspace
// alias (macOS /var -> /private/var, or a workspace-root symlink).
// Resolve only that authorized root, never the requested file:
// internal links must still be rejected by the confined opener.
let canonical_root = root.canonicalize()?;
let relative = requested
.strip_prefix(&root)
.or_else(|_| requested.strip_prefix(&canonical_root))
.map_err(|_| {
std::io::Error::new(
std::io::ErrorKind::PermissionDenied,
"LSP file is outside the workspace",
)
})?;
// Match the existing workspace file serving ceiling. Decode only
// bounded UTF-8 bytes from the same no-follow workspace opener.
const MAX_DOCUMENT_BYTES: u64 = 16 * 1024 * 1024;
let file = crate::fleet::files::WorkspaceFile::open(&canonical_root, relative, false)?;
let mut bytes = Vec::new();
file.open_file()?
.take(MAX_DOCUMENT_BYTES + 1)
.read_to_end(&mut bytes)?;
if bytes.len() as u64 > MAX_DOCUMENT_BYTES {
return Err(std::io::Error::new(
std::io::ErrorKind::InvalidData,
"LSP file exceeds the document limit",
));
}View on GitHub (pinned to 73e0f67d83)