screenpipe/screenpipe · error
Failed to write file {}: {}
Error message
Failed to write file {}: {} What it means
This error wraps an I/O failure from tokio::fs::write while persisting a downloaded file from an MCP directory archive to its target path on disk. The anyhow context adds the destination path and the underlying std::io::Error so you can see both where the write failed and why (permissions, disk full, missing parent dir, etc.).
Source
Thrown at crates/screenpipe-engine/src/cli/mcp.rs:265
.map_err(|e| anyhow::anyhow!("Failed to parse GitHub API response: {}", e))?;
for item in contents {
let target_path = target_dir.join(&item.name);
match item.content_type.as_str() {
"file" => {
if let Some(download_url) = item.download_url {
let file_response = client.get(&download_url).send().await.map_err(|e| {
anyhow::anyhow!("Failed to download file {}: {}", download_url, e)
})?;
let content = file_response
.bytes()
.await
.map_err(|e| anyhow::anyhow!("Failed to get file content: {}", e))?;
tokio::fs::write(&target_path, content).await.map_err(|e| {
anyhow::anyhow!("Failed to write file {}: {}", target_path.display(), e)
})?;
debug!("Downloaded file: {}", target_path.display());
}
}
"dir" => {
tokio::fs::create_dir_all(&target_path).await.map_err(|e| {
anyhow::anyhow!(
"Failed to create directory {}: {}",
target_path.display(),
e
)
})?;
let subdir_api_url = format!(
"https://api.github.com/repos/{}/{}/contents/{}?ref={}",
"screenpipe", "screenpipe", item.path, "main"
);View on GitHub (pinned to 4ebf712990)
Solutions
- Read the wrapped io::Error kind in the message and fix the underlying cause (permissions, disk space, path type).
- Ensure the parent directory exists before writing (create_dir_all on target_path's parent).
- Check that target_path is not an existing directory; remove or pick a different target.
- Retry the download if it was a transient lock (AV scanner, cloud-sync placeholder).
Example fix
// before
tokio::fs::write(&target_path, content).await.map_err(|e| {
anyhow::anyhow!("Failed to write file {}: {}", target_path.display(), e)
})?;
// after
if let Some(parent) = target_path.parent() {
tokio::fs::create_dir_all(parent).await?;
}
tokio::fs::write(&target_path, content).await.map_err(|e| {
anyhow::anyhow!("Failed to write file {}: {}", target_path.display(), e)
})?; Defensive patterns
Strategy: try-catch
Validate before calling
// before download
if let Some(parent) = std::path::Path::new(&target_path).parent() {
tokio::fs::create_dir_all(parent).await?;
} Try / catch
match tokio::fs::write(&target_path, content).await {
Ok(_) => {},
Err(e) if e.kind() == std::io::ErrorKind::PermissionDenied => eprintln!("no write permission at {}", target_path.display()),
Err(e) => return Err(anyhow::anyhow!("write {} failed: {}", target_path.display(), e)),
} Prevention
- Create parent directories before writing downloaded files.
- Run the CLI as a user with write access to the target directory.
- Exclude the download directory from AV/cloud-sync interference.
- Check disk free space for large MCP directories.
When it happens
Trigger: download_mcp_directory fetched a 'file' entry from the archive listing, downloaded its bytes successfully, but tokio::fs::write(&target_path, content) failed — e.g. the parent directory was not created first, the path is not writable, or the disk is full.
Common situations: Downloading an MCP directory into a location without write permissions (~/Library or C:\Program Files), antivirus/backup software locking the file, or a race where the target path exists as a directory so the write fails with IsADirectory.
Understand the failure class
Background: "Permission denied" / "Failed to write" file errors: why a library can't write its files to disk (EACCES, EPERM, ENOSPC) and how to fix them — this error's family across 43 libraries.
Related errors
- Failed to create directory {}: {}
- write {}: {}
- failed to read {}: {error}
- failed to create output directory: {:?}
- createProvider: a non-empty model string is required
AI-assisted analysis of screenpipe/screenpipe@4ebf712990 (2026-09-01).
Data as JSON: /api/errors/342ab5a2494b3a32.
Report an issue: GitHub.