charmbracelet/crush · error
failed to write file: %w
Error message
failed to write file: %w
What it means
io.Copy failed while streaming the HTTP response body into the output file, so the download was aborted partway. Either reading from resp.Body failed (connection reset, context timeout, truncated response) or writing to the file failed (disk full, I/O error).
Source
Thrown at internal/agent/tools/download.go:154
// Create parent directories if they don't exist
if err := os.MkdirAll(filepath.Dir(filePath), 0o755); err != nil {
return fantasy.ToolResponse{}, fmt.Errorf("failed to create parent directories: %w", err)
}
// Create the output file
outFile, err := os.Create(filePath)
if err != nil {
return fantasy.ToolResponse{}, fmt.Errorf("failed to create output file: %w", err)
}
defer outFile.Close()
// Copy data without an explicit size limit.
// The overall download is still constrained by the HTTP client's timeout
// and any upstream server limits.
bytesWritten, err := io.Copy(outFile, resp.Body)
if err != nil {
return fantasy.ToolResponse{}, fmt.Errorf("failed to write file: %w", err)
}
contentType := resp.Header.Get("Content-Type")
responseMsg := fmt.Sprintf("Successfully downloaded %d bytes to %s", bytesWritten, relPath)
if contentType != "" {
responseMsg += fmt.Sprintf(" (Content-Type: %s)", contentType)
}
return fantasy.NewTextResponse(responseMsg), nil
},
)
}
View on GitHub (pinned to 7944b8e522)
Solutions
- Check the wrapped error: context deadline exceeded → raise params.Timeout (max 600)
- Retry the download to overwrite the partial file
- Verify free disk space with df
- Check server-side limits/proxy idle timeouts for large files
Example fix
// before
{ "url": "https://host/big.iso", "file_path": "big.iso", "timeout": 30 }
// after
{ "url": "https://host/big.iso", "file_path": "big.iso", "timeout": 600 } Defensive patterns
Strategy: retry
Validate before calling
// pre-flight free space check
var st syscall.Statfs_t
syscall.Statfs(filepath.Dir(target), &st)
if st.Bavail*uint64(st.Bsize) < expectedSize { return fmt.Errorf("insufficient disk space") } Try / catch
bytesWritten, err := io.Copy(outFile, resp.Body)
if err != nil {
os.Remove(filePath) // drop the truncated partial file
if errors.Is(err, context.DeadlineExceeded) {
return retryWithLongerTimeout()
}
if isRetryableNetErr(err) {
return retryWithBackoff()
}
return fmt.Errorf("download aborted after %d bytes: %w", bytesWritten, err)
} Prevention
- Increase timeout for large downloads (max 600s)
- Ensure sufficient free disk space before big downloads
- Retry interrupted downloads; the tool leaves partial files behind
- Watch for proxies/servers that drop long-running connections
When it happens
Trigger: The request context deadline expired mid-transfer; the server closed the connection early; disk became full during the write; a partial file is left on disk at filePath.
Common situations: Large downloads exceeding the timeout on slow connections; flaky proxies dropping long-lived responses; insufficient disk space; network interruption mid-download. Note the tool leaves a truncated partial file behind.
Related errors
- failed to download from URL: %w
- failed to read response body: %w
- failed to read response: %w
- failed to read response body: %w
- empty providers list from catwalk
AI-assisted analysis of charmbracelet/crush@7944b8e522 (2026-08-29).
Data as JSON: /api/errors/6e7d0bccf3062447.
Report an issue: GitHub.