Hmbown/CodeWhale · error · io::Error
{error}
Error message
{error} What it means
read_workspace_text converts any non-specific I/O failure from the bounded workspace read into io::Error::other, preserving the underlying error as its source. The message is the inner error's Display output. It wraps everything after the size-cap check, including the file open and UTF-8 conversion steps surfaced by the confined opener.
Solutions
- Read the inner source error: re-check the path exists, is a regular file, and is readable before calling.
- For non-UTF-8 files, convert or skip them — the reader only accepts valid UTF-8.
- Retry if the failure was a transient race (file being rewritten); otherwise exclude the path from LSP requests.
Example fix
// before
read_workspace_text(&maybe_missing_path)?;
// after
if path.is_file() { read_workspace_text(&path)? } else { eprintln!("skipping {}", path.display()) } Defensive patterns
Strategy: try-catch
Validate before calling
if !path.is_file() { eprintln!("not a regular file: {path:?}"); }
if std::fs::read(path).map(|b| std::str::from_utf8(&b).is_ok()).unwrap_or(false) { /* ok */ } Type guard
fn readable_utf8_file(p: &Path) -> bool {
std::fs::read(p).map(|b| std::str::from_utf8(&b).is_ok()).unwrap_or(false)
} Try / catch
match read_workspace_text(p) {
Err(e) => { eprintln!("workspace read failed: {e} (source: {:?})", e.source()); fallback_text() }
Ok(t) => use_text(t),
} Prevention
- Verify existence and regular-file type before requesting text.
- Expect races on files being rewritten; retry once or skip.
- Ensure files are valid UTF-8; convert or skip binary files.
When it happens
Trigger: read_workspace_text fails for a reason other than outside-workspace or size-cap: the no-follow open fails (file deleted mid-read, permission error, too many symlink levels, not-a-file), or from_utf8 fails producing an inner UTF-8 error.
Common situations: File removed or renamed between listing and reading; the entry is a directory or FIFO, not a regular file; a symlinked file inside the workspace rejected by the no-follow opener; a binary file passed where UTF-8 text is expected.
Understand the failure class
Background: "failed to read file", EACCES, ENOENT and "could not read <path>" errors: when a program can't read a file from disk — this error's family across 49 libraries.
Related errors
- could not inspect
- could not inspect
- could not read
- external credential path must name a regular file
- failed to open
AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22).
Data as JSON: /api/errors/143fb9c7e6d638ec.
Report an issue: GitHub.
Appendix: source
Thrown at crates/tui/src/lsp/mod.rs:248
"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",
));
}
String::from_utf8(bytes)
.map_err(|error| std::io::Error::new(std::io::ErrorKind::InvalidData, error))
})
.await
.map_err(std::io::Error::other)?
}
/// Inject a fake transport for a language. Used by tests so we never
/// fork a real LSP server in CI.
#[cfg(test)]
pub async fn install_test_transport(&self, lang: Language, transport: Arc<dyn LspTransport>) {
self.test_transports.lock().await.insert(lang, transport);
}
/// Poll the LSP server for diagnostics on `file`. Returns the rendered
/// [`DiagnosticBlock`] (already truncated to the configured per-file
/// max) or `None` when the manager is disabled / has no server / the
/// poll times out.
///
/// The `_edit_seq` argument is currently a no-op; it exists in theView on GitHub (pinned to 73e0f67d83)