xpzouying/xiaohongshu-mcp · error
failed to create save path: %v
Error message
failed to create save path: %v
What it means
NewImageDownloader panics with 'failed to create save path: %v' when os.MkdirAll(savePath, 0755) fails to create (or verify) the directory where downloaded images will be saved. The library throws this eagerly at constructor time so that a bad save path is caught before any download is attempted. Because it uses panic rather than returning an error, an invalid path will crash the calling program unless recovered.
Source
Thrown at pkg/downloader/images.go:28
"path/filepath"
"strings"
"time"
"github.com/h2non/filetype"
"github.com/pkg/errors"
)
// ImageDownloader 图片下载器
type ImageDownloader struct {
savePath string
httpClient *http.Client
}
// NewImageDownloader 创建图片下载器
func NewImageDownloader(savePath string) *ImageDownloader {
// 确保保存目录存在
if err := os.MkdirAll(savePath, 0755); err != nil {
panic(fmt.Sprintf("failed to create save path: %v", err))
}
return &ImageDownloader{
savePath: savePath,
httpClient: &http.Client{
Timeout: 30 * time.Second,
},
}
}
// DownloadImage 下载图片
// 返回本地文件路径
func (d *ImageDownloader) DownloadImage(imageURL string) (string, error) {
// 验证URL格式
if !d.isValidImageURL(imageURL) {
return "", errors.New("invalid image URL format")
}
View on GitHub (pinned to 332d196854)
Solutions
- Verify the savePath value is correct, non-empty, and points to a writable location (check config/env before calling NewImageDownloader).
- Create or fix the directory permissions manually: mkdir -p <savePath> && chmod 755 <savePath> (and chown to the running user if needed).
- If the path exists as a file, remove/rename it so the directory can be created.
- In containers/CI, ensure the volume is mounted read-write and the process user has write access.
- Wrap the call in a recover() at startup to convert the panic into a graceful startup failure with a clear message.
- Call os.MkdirAll(savePath, 0755) yourself first and fail fast with your own error handling before constructing the downloader.
Example fix
// before
downloader := downloader.NewImageDownloader(cfg.ImageSavePath) // panics if path invalid
// after
if err := os.MkdirAll(cfg.ImageSavePath, 0755); err != nil {
log.Fatalf("invalid image save path %q: %v", cfg.ImageSavePath, err)
}
downloader := downloader.NewImageDownloader(cfg.ImageSavePath) Defensive patterns
Strategy: validation
Validate before calling
func ensureSavePath(p string) error {
if p == "" {
return fmt.Errorf("save path is empty")
}
if fi, err := os.Stat(p); err == nil {
if !fi.IsDir() {
return fmt.Errorf("%s exists and is not a directory", p)
}
} else if err := os.MkdirAll(p, 0755); err != nil {
return fmt.Errorf("cannot create %s: %w", p, err)
}
f, err := os.CreateTemp(p, ".writable*")
if err != nil {
return fmt.Errorf("%s is not writable: %w", p, err)
}
f.Close()
os.Remove(f.Name())
return nil
}
// call before: if err := ensureSavePath(cfg.ImageSavePath); err != nil { log.Fatal(err) } Type guard
func isValidSavePath(p string) bool {
if p == "" {
return false
}
fi, err := os.Stat(p)
return err == nil && fi.IsDir()
} Try / catch
func newDownloaderSafe(savePath string) (d *downloader.ImageDownloader, err error) {
defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("image downloader init failed: %v", r)
}
}()
d = downloader.NewImageDownloader(savePath)
return d, nil
} Prevention
- Pre-create the save directory during deployment/setup and verify permissions for the service user.
- Validate path config at application startup, before any component that panics on it.
- In containers, mount save-path volumes read-write and run as a user with write access, not arbitrary UIDs.
- Never pass an unset env var straight into NewImageDownloader; default or reject empty values.
- Add a recover() shim around library constructors that panic instead of returning errors.
When it happens
Trigger: Calling NewImageDownloader(savePath) with: a path whose parent directories are not writable by the current user; a path that exists as a regular file instead of a directory; a savePath containing invalid characters or an empty string ('' resolves to MkdirAll on '' which fails on most platforms); a filesystem that is read-only or full; or a savePath on a network mount that is unavailable.
Common situations: Misconfigured config file pointing the image save directory at a nonexistent/typo path; running the app in a Docker container whose volume mount is missing or read-only; deploying as a non-root service user (e.g. systemd/nobody) that cannot write to a root-owned directory; passing an env var like SAVE_PATH that is unset so it becomes an empty string.
Understand the failure class
Background: mkdir permission denied (EACCES): failed to create directory errors explained — this error's family across 32 libraries.
Related errors
AI-assisted analysis of xpzouying/xiaohongshu-mcp@332d196854 (2026-09-05).
Data as JSON: /api/errors/7da06e184364be64.
Report an issue: GitHub.